package org.codehaus.xfire.jaxb2;

import java.lang.annotation.Annotation;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSchema;
import javax.xml.bind.annotation.XmlType;
import javax.xml.namespace.QName;

import org.codehaus.xfire.MessageContext;
import org.codehaus.xfire.XFireRuntimeException;
import org.codehaus.xfire.aegis.MessageReader;
import org.codehaus.xfire.aegis.MessageWriter;
import org.codehaus.xfire.aegis.stax.ElementReader;
import org.codehaus.xfire.aegis.stax.ElementWriter;
import org.codehaus.xfire.aegis.type.Type;
import org.codehaus.xfire.fault.XFireFault;
import org.jdom.Element;

public class JaxbType
    extends Type
{
    private JAXBContext context;

    public JaxbType(Class clazz)
    {
        setTypeClass(clazz);

        initType();
    }

    public Object readObject(MessageReader reader, MessageContext context)
        throws XFireFault
    {
        try
        {
            JAXBContext jc = getJAXBContext();

            Unmarshaller u = jc.createUnmarshaller();
            return u.unmarshal(((ElementReader) reader).getXMLStreamReader());
        }
        catch (JAXBException e)
        {
            throw new XFireFault("Could not unmarshall type.", e, XFireFault.RECEIVER);
        }

    }

    public void writeObject(Object object, MessageWriter writer, MessageContext context)
        throws XFireFault
    {
        try
        {
            JAXBContext jc = getJAXBContext();

            Marshaller m = jc.createMarshaller();
            m.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
            m.marshal(object, ((ElementWriter) writer).getXMLStreamWriter());
        }
        catch (JAXBException e)
        {
            throw new XFireFault("Could not unmarshall type.", e, XFireFault.RECEIVER);
        }
    }

    public JAXBContext getJAXBContext()
        throws JAXBException
    {
        if (context == null)
        {
            context = JAXBContext.newInstance(getTypeClass());
        }
        return context;
    }

    @Override
    public boolean isComplex()
    {
        return true;
    }

    @Override
    public boolean isWriteOuter()
    {
        return false;
    }

    @Override
    public QName getSchemaType()
    {
        return super.getSchemaType();
    }

    public void initType()
    {
        Class clazz = getTypeClass();
        XmlRootElement root = (XmlRootElement) clazz.getAnnotation(XmlRootElement.class);
        XmlType type = (XmlType) clazz.getAnnotation(XmlType.class);
        String local = null;
        String nsUri = null;
        
        if (root != null)
        {
            setAbstract(false);
            local = root.name();
            nsUri = root.namespace();
        }
        else if (type != null)
        {
            setAbstract(true);
            local = type.name();
            nsUri = type.namespace();
        }
        else
        {
            throw new XFireRuntimeException("Couldn't determine element name.");
        }
        
        if (local.equals("##default"))
        {
            local = clazz.getSimpleName();
        }
        
        if (nsUri.equals("##default"))
        {
            nsUri = getPackageNs(clazz);
        }

        setSchemaType(new QName(nsUri, local));
    }

    public static String getPackageNs(Class clazz)
    {
        Package pack = clazz.getPackage();
        XmlSchema schema = pack.getAnnotation(XmlSchema.class);
        String namespace = null;
        if (schema != null)
        {
            namespace = schema.namespace();
        }
        else
        {
            namespace = "";
        }
        return namespace;
    }

    /**
     * JAXB doesn't retain all the schema information at runtime, so schemas
     * must be added manually. So, this method does absolutely nothing.
     */
    @Override
    public void writeSchema(Element root)
    {
    }
}

@@ -43,37 +43,31 @@
 java
-.lang.annotation.Annotation;%0A
+x.xml.bind.JAXBContext;
 %0Aimp
@@ -81,38 +81,38 @@
 ax.xml.bind.JAXB
-Contex
+Elemen
 t;%0Aimport javax.
@@ -564,60 +564,8 @@
 er;%0A
-import org.codehaus.xfire.aegis.stax.ElementReader;%0A
 impo
@@ -888,32 +888,67 @@
 tType();%0A    %7D%0A%0A
+    @SuppressWarnings(%22unchecked%22)%0A
     public Objec
@@ -1170,24 +1170,259 @@
             
+if (isAbstract()) %0A            %7B%0A                 JAXBElement element = u.unmarshal(reader.getXMLStreamReader(), getTypeClass());%0A                 return element.getValue();%0A            %7D%0A            else%0A            %7B%0A                
 return u.unm
@@ -1432,32 +1432,14 @@
 hal(
-((ElementReader) 
 reader
-)
 .get
@@ -1454,24 +1454,38 @@
 mReader());%0A
+            %7D%0A
         %7D%0A  
@@ -2642,32 +2642,71 @@
 maType();%0A    %7D%0A
+    %0A    @SuppressWarnings(%22unchecked%22)
 %0A    public void

support abstract jaxb types