
package example22;

import java.io.OutputStream;
import java.io.Writer;

import org.jibx.runtime.BindingDirectory;
import org.jibx.runtime.IBindingFactory;
import org.jibx.runtime.IMarshallable;
import org.jibx.runtime.IUnmarshallingContext;
import org.jibx.runtime.JiBXException;
import org.jibx.runtime.impl.MarshallingContext;
import org.jibx.runtime.impl.UnmarshallingContext;

public class BindingSelector
{
    /** URI of version selection attribute. */
    private final String m_attributeUri;
    
    /** Name of version selection attribute. */
    private final String m_attributeName;
    
    /** Array of version names. */
    private final String[] m_versionTexts;
    
    /** Array of bindings corresponding to versions. */
    private final String[] m_versionBindings;
    
    /** Basic unmarshalling context used to determine document version. */
    private final UnmarshallingContext m_context;
    
    /** Stream for marshalling output. */
    private OutputStream m_outputStream;
    
    /** Encoding for output stream. */
    private String m_outputEncoding;
    
    /** Output writer for marshalling. */
    private Writer m_outputWriter;
    
    /** Indentation for marshalling. */
    private int m_outputIndent;
    
    /**
     * Constructor.
     *
     * @param uri version selection attribute URI (<code>null</code> if none)
     * @param name version selection attribute name
     * @param versions array of version texts (first is default)
     * @param bindings array of binding names corresponding to versions
     */
    
    public BindingSelector(String uri, String name, String[] versions,
        String[] bindings) {
        m_attributeUri = uri;
        m_attributeName = name;
        m_versionTexts = versions;
        m_versionBindings = bindings;
        m_context = new UnmarshallingContext();
        m_outputIndent = -1;
    }
    
    /**
     * Get initial unmarshalling context. This gives access to the unmarshalling
     * context used before the specific version is determined. The document
     * information must be set for this context before calling {@link
     * #unmarshalVersioned}.
     *
     * @return initial unmarshalling context
     */
    
    public IUnmarshallingContext getContext() {
        return m_context;
    }
    
    /**
     * Set output stream and encoding.
     *
     * @param outs stream for document data output
     * @param enc document output encoding, or <code>null</code> for default
     */
     
    void setOutput(OutputStream outs, String enc) {
        m_outputStream = outs;
        m_outputEncoding = enc;
    }
    
    /**
     * Set output writer.
     *
     * @param outw writer for document data output
     */
    
    void setOutput(Writer outw) {
        m_outputWriter = outw;
    }
    
    /**
     * Set nesting indent spaces.
     *
     * @param indent number of spaces to indent per level, or disable
     * indentation if negative
     */
    
    void setIndent(int indent) {
        m_outputIndent = indent;
    }
    
    /**
     * Marshal according to supplied version.
     *
     * @param obj root object to be marshalled
     * @param version identifier for version to be used in marshalling
     * @throws JiBXException if error in marshalling
     */
    
    public void marshalVersioned(Object obj, String version)
        throws JiBXException {
        
        // look up version in defined list
        String match = (version == null) ? m_versionTexts[0] : version;
        for (int i = 0; i < m_versionTexts.length; i++) {
            if (match.equals(m_versionTexts[i])) {
                
                // version found, create marshaller for the associated binding
                IBindingFactory fact = BindingDirectory.
                    getFactory(m_versionBindings[i], obj.getClass());
                MarshallingContext context =
                    (MarshallingContext)fact.createMarshallingContext();
                
                // configure marshaller for writing document
                context.setIndent(m_outputIndent);
                if (m_outputWriter == null) {
                    if (m_outputStream == null) {
                        throw new JiBXException("Output not configured");
                    } else {
                        context.setOutput(m_outputStream, m_outputEncoding);
                    }
                } else {
                    context.setOutput(m_outputWriter);
                }
                
                // output object as document
                context.startDocument(m_outputEncoding, null);
                ((IMarshallable)obj).marshal(context);
                context.endDocument();
                return;
                
            }
        }
        
        // error if unknown version in document
        throw new JiBXException("Unrecognized document version " + version);
    }
    
    /**
     * Unmarshal according to document version.
     *
     * @param clas expected class mapped to root element of document (used only
     * to look up the binding)
     * @return root object unmarshalled from document
     * @throws JiBXException if error in unmarshalling
     */
    
    public Object unmarshalVersioned(Class clas) throws JiBXException {
        
        // get the version attribute value (using first value as default)
        m_context.toStart();
        String version = m_context.attributeText(m_attributeUri,
            m_attributeName, m_versionTexts[0]);
        
        // look up version in defined list
        for (int i = 0; i < m_versionTexts.length; i++) {
            if (version.equals(m_versionTexts[i])) {
                
                // version found, create unmarshaller for the associated binding
                IBindingFactory fact = BindingDirectory.
                    getFactory(m_versionBindings[i], clas);
                UnmarshallingContext context =
                    (UnmarshallingContext)fact.createUnmarshallingContext();
                
                // return object unmarshalled using binding for document version
                context.setFromContext(m_context);
                return context.unmarshalElement();
                
            }
        }
        
        // error if unknown version in document
        throw new JiBXException("Unrecognized document version " + version);
    }
}