/*
 * blanco Framework
 * Copyright (C) 2004-2006 IGA Tosiki
 * 
 * This library 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.
 */
package blanco.xml.bind;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.TransformerHandler;
import javax.xml.transform.stream.StreamResult;

import org.xml.sax.SAXException;

import blanco.commons.util.BlancoStringUtil;
import blanco.xml.bind.valueobject.BlancoXmlDocument;

/**
 * JavaオブジェクトからXMLを生成します。
 * 
 * このクラスは XML/バリューオブジェクトマッピング (X/Oマッピング) blancoXmlBinding の一部です。
 * 
 * @author IGA Tosiki
 */
public class BlancoXmlMarshaller {
    /**
     * 内部的に利用する出力用SAXハンドラ
     * 
     * 連結先のストリームは外部でcloseなどの処理が実施される必要があります。
     */
    private TransformerHandler fSaxHandler;

    /**
     * JavaオブジェクトからXMLを生成します。
     * 
     * @param document
     *            blancoXmlとしてのオブジェクト。
     * @param outFile
     *            XML出力先ファイル。
     */
    public void marshal(final BlancoXmlDocument document, final File outFile) {
        if (document == null) {
            throw new IllegalArgumentException(
                    "BlancoXmlMarshaller: 入力XMLドキュメントにnullが渡されました。");
        }
        if (outFile == null) {
            throw new IllegalArgumentException(
                    "BlancoXmlMarshaller: 出力先XMLファイルにnullが渡されました。");
        }
        if (outFile.exists()) {
            if (outFile.canWrite() == false) {
                throw new IllegalArgumentException(
                        "BlancoXmlMarshaller: 出力先XMLファイルは書き込みできません。");
            }
        }

        try {
            final OutputStream outStream = new BufferedOutputStream(
                    new FileOutputStream(outFile));
            try {
                marshal(document, outStream);

                outStream.flush();
            } finally {
                outStream.close();
            }
        } catch (IOException ex) {
            throw new IllegalArgumentException(
                    "BlancoXmlMarshaller: ファイル出力に失敗しました。" + ex.toString());
        }
    }

    /**
     * JavaオブジェクトからXMLを生成します。
     * 
     * 参考:
     * http://java.sun.com/webservices/docs/1.6/api/javax/xml/bind/Marshaller
     * .html
     * 
     * @param document
     *            blancoXmlとしてのオブジェクト。
     * @param outStream
     *            XML出力先ストリーム。
     */
    public void marshal(final BlancoXmlDocument document,
            final OutputStream outStream) {
        final TransformerFactory tf = TransformerFactory.newInstance();
        final SAXTransformerFactory saxTf = (SAXTransformerFactory) tf;
        try {
            fSaxHandler = saxTf.newTransformerHandler();
            if (BlancoStringUtil.null2Blank(document.getVersion()).length() > 0) {
                fSaxHandler.getTransformer().setOutputProperty("version",
                        document.getVersion());
            }
            if (BlancoStringUtil.null2Blank(document.getEncoding()).length() > 0) {
                fSaxHandler.getTransformer().setOutputProperty("encoding",
                        document.getEncoding());
            }
        } catch (TransformerConfigurationException e) {
            e.printStackTrace();
            throw new IllegalArgumentException(
                    "BlancoXmlMarshaller: トランスフォーマーハンドラ生成に失敗しました。"
                            + e.toString());
        }

        fSaxHandler.setResult(new StreamResult(outStream));

        try {
            new BlancoXmlMarshallerSerializer(fSaxHandler).serialize(document);
        } catch (SAXException e) {
            e.printStackTrace();
            throw new IllegalArgumentException(
                    "BlancoXmlMarshaller: オブジェクトからXMLの変換の過程で例外が発生しました。"
                            + e.toString());
        }

        // 最後にハンドラを開放します。
        fSaxHandler = null;
    }
}
