/*
 * JBoss, Home of Professional Open Source
 * Copyright 2005, JBoss Inc., and individual contributors as indicated
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This 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 (at your option) any later version.
 *
 * This software 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 software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.jboss.ws.core.jaxrpc.binding.jbossxb;

// $Id: JBossXBMarshallerImpl.java 3959 2007-07-20 14:44:19Z heiko.braun@jboss.com $

import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.HashMap;

import javax.xml.namespace.QName;

import org.jboss.logging.Logger;
import org.jboss.util.NotImplementedException;
import org.jboss.ws.WSException;
import org.jboss.ws.extensions.xop.jaxrpc.XOPMarshallerImpl;
import org.jboss.ws.metadata.jaxrpcmapping.JavaWsdlMapping;
import org.jboss.ws.metadata.jaxrpcmapping.JavaXmlTypeMapping;
import org.jboss.ws.metadata.jaxrpcmapping.VariableMapping;
import org.jboss.wsf.common.JavaUtils;
import org.jboss.xb.binding.Constants;
import org.jboss.xb.binding.sunday.marshalling.MarshallerImpl;
import org.jboss.xb.binding.sunday.unmarshalling.SchemaBinding;
import org.w3c.dom.Node;
import org.xml.sax.ContentHandler;

/**
 * An implementation of a JAXB Marshaller that uses the JBossXB schema binding marshaller.
 *
 * @author Thomas.Diesler@jboss.org
 * @author Heiko.Braun@jboss.org
 * @since 05-Jul-2006
 */
public class JBossXBMarshallerImpl implements JBossXBMarshaller {

   // provide logging
   private static final Logger log = Logger.getLogger(JBossXBMarshallerImpl.class);

   // The marshaller properties
   private HashMap properties = new HashMap();

   private MarshallerImpl delegate;

   public JBossXBMarshallerImpl()
   {

      //ClassInfos.disableCache();

      delegate = new MarshallerImpl();
      delegate.setProperty(org.jboss.xb.binding.Marshaller.PROP_OUTPUT_XML_VERSION, "false");
      delegate.setProperty(org.jboss.xb.binding.Marshaller.PROP_OUTPUT_INDENTATION, "false");
      delegate.declareNamespace("xsi", Constants.NS_XML_SCHEMA_INSTANCE);
      delegate.setSupportNil(true);
   }

   /**
    * Marshal the content tree rooted at obj into a Writer.
    */
   public void marshal(Object obj, Writer writer) throws MarshalException
   {
      assertRequiredProperties();

      try
      {
         QName xmlName = (QName)getProperty(JBossXBConstants.JBXB_ROOT_QNAME);
         delegate.addRootElement(xmlName);

         QName xmlType = (QName)getProperty(JBossXBConstants.JBXB_TYPE_QNAME);
         boolean isAnonymousType = (xmlType != null && xmlType.getLocalPart().startsWith(">"));
         if (xmlType != null && !isAnonymousType)
         {
            delegate.setRootTypeQName(xmlType);
         }

         if (xmlName.getNamespaceURI().length() > 0)
         {
            String prefix = xmlName.getPrefix();
            String nsURI = xmlName.getNamespaceURI();
            delegate.declareNamespace(prefix, nsURI);
         }

         // wildcards still need to be mapped
         // todo: cleanup XB API
         JavaWsdlMapping wsdlMapping = (JavaWsdlMapping)getProperty(JBossXBConstants.JBXB_JAVA_MAPPING);
         if (wsdlMapping != null)
         {
            JavaXmlTypeMapping[] javaXmlMappings = wsdlMapping.getJavaXmlTypeMappings();
            if (javaXmlMappings != null)
            {
               for (int i = 0; i < javaXmlMappings.length; ++i)
               {
                  JavaXmlTypeMapping javaXmlMapping = javaXmlMappings[i];
                  VariableMapping[] variableMappings = javaXmlMapping.getVariableMappings();

                  if (variableMappings != null)
                  {
                     String clsName = javaXmlMapping.getJavaType();
                     Class cls = JavaUtils.loadJavaType(clsName, Thread.currentThread().getContextClassLoader());
                     QName clsQName = javaXmlMapping.getRootTypeQName();

                     if (clsQName != null)
                     {
                        // TODO: legacy API usage, see JBWS-1091
                        if ("complexType".equalsIgnoreCase(javaXmlMapping.getQnameScope()))
                        {
                           delegate.mapClassToXsiType(cls, clsQName.getNamespaceURI(), clsQName.getLocalPart());
                        }
                     }

                     for (int j = 0; j < variableMappings.length; ++j)
                     {
                        VariableMapping variableMapping = variableMappings[j];
                        if (variableMapping.getXmlWildcard())
                        {
                           delegate.mapFieldToWildcard(cls, "_any", JBossXBSupport.getWildcardMarshaller());
                        }
                     }
                  }
               }
            }
         }

         // the actual marshalling
         SchemaBinding schemaBinding = JBossXBSupport.getOrCreateSchemaBinding(properties);
         schemaBinding.setXopMarshaller(new XOPMarshallerImpl());
         delegate.marshal(schemaBinding, null, obj, writer);
      }
      catch (RuntimeException e)
      {
         throw e;
      }
      catch (Exception e)
      {
         throw new MarshalException(e);
      }
   }

   /**
    * Marshal the content tree rooted at obj into SAX2 events.
    */
   public void marshal(Object obj, ContentHandler handler)
   {
      throw new NotImplementedException();
   }

   /**
    * Marshal the content tree rooted at obj into a DOM tree.
    */
   public void marshal(Object obj, Node node)
   {
      throw new NotImplementedException();
   }

   /**
    * Marshal the content tree rooted at obj into an output stream.
    */
   public void marshal(Object obj, OutputStream os) throws MarshalException
   {
      marshal(obj, new OutputStreamWriter(os));
   }

   /**
    * Get the particular property in the underlying implementation of
    * Marshaller.
    */
   public Object getProperty(String name)
   {
      if (name == null)
         throw new IllegalArgumentException("name parameter is null");

      return properties.get(name);
   }

   /**
    * Set the particular property in the underlying implementation of
    * Marshaller.
    *
    */
   public void setProperty(String name, Object value)
   {
      if (name == null)
         throw new IllegalArgumentException("name parameter is null");

      properties.put(name, value);
   }

   /**
    * Get a DOM tree view of the content tree(Optional).
    */
   public Node getNode(Object contentTree)
   {
      throw new NotImplementedException();
   }

   /**
    * Assert the required properties
    */
   private void assertRequiredProperties()
   {
      if (getProperty(JBossXBConstants.JBXB_SCHEMA_READER) == null && getProperty(JBossXBConstants.JBXB_XS_MODEL) == null)
         throw new WSException("Cannot find required property: " + JBossXBConstants.JBXB_XS_MODEL);

      if (getProperty(JBossXBConstants.JBXB_JAVA_MAPPING) == null)
         throw new WSException("Cannot find required property: " + JBossXBConstants.JBXB_JAVA_MAPPING);

      QName xmlName = (QName)getProperty(JBossXBConstants.JBXB_ROOT_QNAME);
      if (xmlName == null)
         throw new WSException("Cannot find required property: " + JBossXBConstants.JBXB_ROOT_QNAME);

      if (xmlName.getNamespaceURI().length() > 0 && xmlName.getPrefix().length() == 0)
         throw new IllegalArgumentException("The given root element name must be prefix qualified: " + xmlName);
   }
}
