/*
 * Copyright (c) 2002-2003 IST-2004-2006-511731 ModelWare - ModelBus.
 * All rights reserved.
 *
 * This software is published under the terms of the ModelBus Software License
 * 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.
 * A copy of ModelBus Software License is provided with this distribution in
 * doc/LICENSE.txt file.
 */


/*
 * Created on 21 mai 2005
 *
 */
package org.eclipse.mddi.modelbus.adapter.infrastructure.transport.marshal.ws;


import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;

import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPFault;

import org.apache.log4j.Logger;
import org.eclipse.mddi.modelbus.adapter.infrastructure.serialize.Serializer;
import org.eclipse.mddi.modelbus.adapter.infrastructure.serialize.emf_xmi.DefaultSerializer;
import org.eclipse.mddi.modelbus.adapter.user.ModelingServiceError;
import org.eclipse.mddi.modelbus.description.abstract_.EnumerationType;
import org.eclipse.mddi.modelbus.description.abstract_.ModelType;
import org.eclipse.mddi.modelbus.description.abstract_.Parameter;
import org.eclipse.mddi.modelbus.description.abstract_.PrimitiveType;
import org.eclipse.mddi.modelbus.description.abstract_.Type;






/**
 * Manages the marshaling and unmarshaling of basic parameter values (int, string, boolean etc).
 * The marshaling of models is to be defined in the subclasses.
 * 
 * @author Prawee Sriplakich (LIP6)
 *
 */
public abstract class AbstractMarshaler implements Marshaler {
    
    private static Logger logger = Logger.getLogger(AbstractMarshaler.class);
    
    Serializer serializer;
    
  
    public AbstractMarshaler() {
        serializer = new DefaultSerializer ();
    }

    
	/* (non-Javadoc)
     * @see org.eclipse.mddi.modelbus.adapter.infrastructure.transport.marshal.ws.ParameterMarshaler#marshall(org.eclipse.mddi.modelbus.description.abstract_.Parameter, java.lang.Object, javax.xml.soap.SOAPElement)
     */
	public void marshal(Parameter p, Object parameterValue, SOAPElement topElem) throws Exception {
		//if parameter value is null, do nothing
	    if(parameterValue==null) {
		  return;    
		}
        SOAPElement paramSoapElem = topElem.addChildElement(p.getName());
        if(p.getUpper()!=1) {
            if(!(parameterValue instanceof Object[])) {
                throw new SOAPException("Marshall error, invalid parameter multiplicity - " + p.getName());
            }
            Object[] values = (Object[]) parameterValue;
            for(int i=0; i < values.length; i++) {
                Object singleValue = values[i];
                SOAPElement itemElem = paramSoapElem.addChildElement("item");
                marshallSingleValue(p, singleValue, itemElem);
            }        
        } else {
            marshallSingleValue(p, parameterValue, paramSoapElem);
        }
	}
	
	
	void marshallSingleValue(Parameter p, Object value, SOAPElement elem) throws Exception {
	    Type t= p.getType();
        String serializedObject;
        serializedObject = serializer.serialize(p,value);
	    if(t instanceof PrimitiveType) {
	        marshallPrimitiveSingleValue(p, serializedObject, elem);
	    } else if(t instanceof EnumerationType) {
	        elem.addTextNode((String)serializedObject);
	        elem.setEncodingStyle("modelbus:Enumeration");
	    } else if(t instanceof ModelType) {
	        marshalModel(p, value, elem);	    
	    }	    
	}
	
	
	static void marshallPrimitiveSingleValue(Parameter p, Object value, SOAPElement elem) throws SOAPException {
		Type t= p.getType();
		String typeName = t.getName();
      try {
		if(typeName.equalsIgnoreCase("string")) {
            elem.setAttribute("xsi:type", "xsd:string");
            elem.addTextNode((String)value);            
        } else if(typeName.equalsIgnoreCase("integer")) {
            elem.setAttribute("xsi:type", "xsd:integer");
            elem.addTextNode((String)value);
        } else if(typeName.equalsIgnoreCase("double")) {
	        elem.setAttribute("xsi:type", "xsd:float"); 
            elem.addTextNode((String)value);	        
        } else if(typeName.equalsIgnoreCase("boolean")) {
	        elem.setAttribute("xsi:type", "xsd:boolean"); 
            elem.addTextNode((String)value);	        	        
        } else {
          throw new SOAPException("Marshall error, unknown PrimitiveType " + typeName);    
        }
      } catch(ClassCastException e) {
          logger.debug("marshal : " + typeName+":"+ p.getName()+"="+value +"(" +value.getClass() +")");
          throw new SOAPException("Marshall error, invalid primitive type - " + typeName+":"+ p.getName(), e);
      }
	}
	
	 
	/* (non-Javadoc)
     * @see org.eclipse.mddi.modelbus.adapter.infrastructure.transport.marshal.ws.ParameterMarshaler#unmarshall(org.eclipse.mddi.modelbus.description.abstract_.Parameter, javax.xml.soap.SOAPElement)
     */
	public Object unmarshal(Parameter p, SOAPElement topElem) throws Exception {
		SOAPElement elem = SoapUtil.getChildByLocalName(topElem, p.getName());
        // if the element is not found in SOAP message, then the parameter value is null
		if(elem==null)  {
          return null;
        }
        if(p.getUpper()!=1) {
            List values = new Vector();
            Iterator it = elem.getChildElements();
            while(it.hasNext()) {
                Object o = it.next();
                if(o instanceof SOAPElement) {
                    SOAPElement subElem = (SOAPElement) o;
                    Object v = unmarshallSingleValue(p, subElem);
                    values.add(v);  
                }
            }
            Object[] array = getArrayForMultiValueParameter(p, values.size());
            return values.toArray(array);            
        } else {
            return unmarshallSingleValue(p, elem);
        }
	}
	
	
	/**
	 * Creates array for storing parameter values
	 * 
	 * @param p
	 * @param size
	 * @return
	 */
	static Object[] getArrayForMultiValueParameter(Parameter p, int size) {
		Type t= p.getType();
	    if(t instanceof PrimitiveType) {
	        String typeName = t.getName();
	        if(typeName.equalsIgnoreCase("string")) {
	            return new String[size];
	        } else if(typeName.equalsIgnoreCase("integer")) {
	            return new Integer[size];
	        } else if(typeName.equalsIgnoreCase("double")) {
	            return new Double[size];	            
	        } else if(typeName.equalsIgnoreCase("boolean")) {
	            return new Boolean[size];	        
	        } else {
	          return null;
	        }	
	    } else if(t instanceof EnumerationType) {
            return new String[size];
	    } else if(t instanceof ModelType) {
	        return new Collection[size];
	    }
        return null;
	}
	
	
	
	Object unmarshallSingleValue(Parameter p, SOAPElement elem) throws Exception {
		Type t= p.getType();
        String serializedObject=null;
        
	    if(t instanceof PrimitiveType) {
            serializedObject = elem.getValue();
	    } else if(t instanceof EnumerationType) {
            String vString = elem.getValue();
            serializedObject = vString;
	    } else if(t instanceof ModelType) {
            serializedObject = (String) unmarshalModel(p, elem);
	    }
        
        return serializer.deserialize(p,serializedObject); 
	}
	
	
	
	
	

	
	public static void marshallModelingServiceError(ModelingServiceError err, SOAPFault f) throws SOAPException {
	    f.setFaultCode("modelbus:ModelingServiceError");
	    f.setFaultString(err.getName()); 
	}
	
	
	/**
	 * Identifies whether this SOAP fault contains ModelingServiceError or other kinds of errors. 
	 * 
	 * @param f
	 * @return
	 * 
	 */
	public static boolean isModelingServiceError(SOAPFault f) {
	    String fcode = f.getFaultCode();
	    return fcode.equals("ModelingServiceError");
	}
	
	public static ModelingServiceError unmarshallModelingServiceError(SOAPFault f) {
	    String errName = f.getFaultString();
	    ModelingServiceError err = new ModelingServiceError(errName, null);
	    return err;
	}
	
	
	/**
	 * Set namespace declarations used for defining encodings 
	 * 
	 * @param body
	 * @throws SOAPException
	 */
	public static void setNamespaces(SOAPBody body) throws SOAPException {
	    body.addNamespaceDeclaration("xsd", "http://www.w3.org/1999/XMLSchema");
	    body.addNamespaceDeclaration("xsi", "http://www.w3.org/1999/XMLSchema-instance");
	    body.addNamespaceDeclaration("modelbus", "http://www.eclipse.org/mddi/modelbus");	    
	}


    /* (non-Javadoc)
     * @see org.eclipse.mddi.modelbus.adapter.infrastructure.transport.marshal.ws.Marshaler#marshal(org.eclipse.mddi.modelbus.description.abstract_.Parameter[], java.lang.Object[], javax.xml.soap.SOAPElement)
     */
    public void marshal(Parameter[] params, Object[] parameterValues, SOAPElement topElem) throws Exception {
        if (params==null) return;
        for (int i = 0; i < params.length; i++) 
            marshal(params[i], parameterValues[i], topElem);
    }


    /* (non-Javadoc)
     * @see org.eclipse.mddi.modelbus.adapter.infrastructure.transport.marshal.ws.Marshaler#unmarshal(org.eclipse.mddi.modelbus.description.abstract_.Parameter[], javax.xml.soap.SOAPElement)
     */
    public Object[] unmarshal(Parameter[] params, SOAPElement topElem) throws Exception {
        if (params==null) return null;
        Object [] unmarshaledParams = new Object [params.length];
        for(int i=0; i<params.length; i++) 
            unmarshaledParams[i]=unmarshal(params[i], topElem);
        return unmarshaledParams;
    }


    /**
     * @return Returns the serializer.
     */
    public Serializer getSerializer() {
        return serializer;
    }


    /**
     * @param serializer The serializer to set.
     */
    public void setSerializer(Serializer serializer) {
        this.serializer = serializer;
    }
	
	
}
