/*
 * Copyright 2006-2007 Queplix Corp.
 *
 * Licensed under the Queplix Public License, Version 1.1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.queplix.com/solutions/commercial-open-source/queplix-public-license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 *
 */

package com.queplix.core.utils.xml;

import com.queplix.core.error.GenericSystemException;
import com.queplix.core.utils.StringHelper;
import org.w3c.dom.Document;

import javax.servlet.ServletInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.CharArrayWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;

/**
 * Helper class for XML binding.
 *
 * @author [ALB] Baranov Andrey
 * @author [ONZ] Oleg N. Zhovtanyuk
 * @version $Revision: 1.1.1.1 $ $Date: 2005/09/12 15:31:25 $
 */

public final class XMLHelper {

    // ===================================================== Constants

    private static boolean DEF_VALIDATE = false;

    // ===================================================== Constructor

    private XMLHelper() {
    }

    // ===================================================== Public methods

    /**
     * Produces the Java object from the XML source.
     *
     * @param cl Java class
     * @param in byte input stream to read XML from
     * @return Java object
     */
    public static Object getParsedObject(Class cl, InputStream in) {
        return getParsedObject(cl, in, DEF_VALIDATE);
    }

    /**
     * Produces the Java object from an XML source.
     *
     * @param cl       Java class
     * @param in       byte input stream to read XML from
     * @param validate whether to validate the source
     * @return Java object
     */
    public static Object getParsedObject(Class cl, InputStream in,
                                         boolean validate) {
        Document document = XMLFactory.getXMLWrapper().getDocument(in,
                validate);
        XMLBinding xmlBinding = XMLFactory.getXMLBinding();
        return xmlBinding.xmlToJava(cl, document);
    }

    /**
     * Produces the Java object from the XML source.
     *
     * @param cl Java class
     * @param in character input stream to read XML from
     * @return Java object
     */
    public static Object getParsedObject(Class cl, Reader in) {
        return getParsedObject(cl, in, DEF_VALIDATE);
    }

    /**
     * Produces the Java object from an XML source.
     *
     * @param cl       Java class
     * @param in       character input stream to read XML from
     * @param validate whether to validate the source
     * @return Java object
     */
    public static Object getParsedObject(Class cl, Reader in,
                                         boolean validate) {
        Document document = XMLFactory.getXMLWrapper().getDocument(in,
                validate);
        XMLBinding xmlBinding = XMLFactory.getXMLBinding();
        return xmlBinding.xmlToJava(cl, document);
    }

    /**
     * Produces the Java object from an XML source.
     *
     * @param cl Java class
     * @param in servlet input stream to read XML from
     * @return Java object
     */
    public static Object getParsedObject(Class cl, ServletInputStream in) {
        return getParsedObject(cl, in, DEF_VALIDATE);
    }

    /**
     * Produces the Java object from an XML source.
     *
     * @param cl       Java class
     * @param in       servlet input stream to read XML from
     * @param validate whether to validate the source
     * @return Java object
     */
    public static Object getParsedObject(Class cl, ServletInputStream in,
                                         boolean validate) {

        ByteArrayOutputStream baos = null;
        ByteArrayInputStream bais = null;
        Document document = null;

        try {

            // Copy all data to the buffer.
            baos = new ByteArrayOutputStream();
            int c = 0;
            byte[] data = new byte[4096];
            while((c = in.read(data)) != -1) {
                baos.write(data, 0, c);
            }
            baos.flush();
            bais = new ByteArrayInputStream(baos.toByteArray());
            baos.close();

            // Parse document.
            document = XMLFactory.getXMLWrapper().getDocument(bais, validate);

        } catch (IOException ex) {
            throw new GenericSystemException(
                    "I/O Exception: " + ex.getMessage(), ex);
        } finally {
            try {
                if(baos != null) {
                    baos.close();
                }
            } catch (Exception ex) {
            }
            try {
                if(bais != null) {
                    bais.close();
                }
            } catch (Exception ex) {
            }
        }

        XMLBinding xmlBinding = XMLFactory.getXMLBinding();
        return xmlBinding.xmlToJava(cl, document);

    }

    /**
     * Serializes the Java object to the given output stream.
     *
     * @param o   Java object to serialize
     * @param out out writer
     */
    public static void writeObject(Object o, Writer out) {
        writeObject(o, out, false);
    }

    /**
     * Serializes the Java object to the given output stream.
     *
     * @param o        Java object to serialize
     * @param out      out writer
     * @param clearXml clear or not result XML
     */
    public static void writeObject(Object o, Writer out, boolean clearXml) {
        XMLBinding xmlBinding = XMLFactory.getXMLBinding();

        Writer tmpWriter = null;
        try {

            if(clearXml) {
                // use temp writer
                tmpWriter = new StringWriter();
                xmlBinding.javaToXml(o, tmpWriter);

                // remove <?xml ..?> header only
                String s = tmpWriter.toString();
                s = StringHelper.clearXml(s, false);

                // out to orig writer
                out.write(s);
            } else {
                // out directly to orig writer
                xmlBinding.javaToXml(o, out);
            }

        } catch (GenericSystemException ex) {
            throw ex;

        } catch (IOException ex) {
            throw new GenericSystemException("IO exception: " + ex.getMessage(),
                    ex);

        } catch (Throwable t) {
            t.printStackTrace();
            throw new GenericSystemException(
                    "Unknown exception: " + t.getMessage(), t);

        } finally {
            try {
                if(tmpWriter != null) {
                    tmpWriter.close();
                }
            } catch (Exception e) {
            }
        }
    }

    /**
     * Serializes the Java object to the given character array.
     *
     * @param o Java object to serialize
     * @return character array
     */
    public static char[] writeObject(Object o) {
        CharArrayWriter caw = new CharArrayWriter();
        try {
            writeObject(o, caw);
            return caw.toCharArray();
        } finally {
            caw.close();
        }
    }

}
