/*******************************************************************************
 * Copyright (c) 2008 IGA Tosiki, NTT DATA BUSINESS BRAINS Corp.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    IGA Tosiki (NTT DATA BUSINESS BRAINS Corp.) - initial API and implementation
 *******************************************************************************/
/*
 * blanco Framework
 * Copyright (C) 2008 NTT DATA BUSINESS BRAINS CORPORATION
 * 
 * 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.xliff;

import java.io.File;
import java.io.OutputStream;

import blanco.commons.util.BlancoStringUtil;
import blanco.xliff.valueobject.BlancoXliff;
import blanco.xliff.valueobject.BlancoXliffAltTrans;
import blanco.xliff.valueobject.BlancoXliffBody;
import blanco.xliff.valueobject.BlancoXliffFile;
import blanco.xliff.valueobject.BlancoXliffHeader;
import blanco.xliff.valueobject.BlancoXliffNote;
import blanco.xliff.valueobject.BlancoXliffTarget;
import blanco.xliff.valueobject.BlancoXliffTool;
import blanco.xliff.valueobject.BlancoXliffTransUnit;
import blanco.xml.bind.BlancoXmlMarshaller;
import blanco.xml.bind.valueobject.BlancoXmlAttribute;
import blanco.xml.bind.valueobject.BlancoXmlCharacters;
import blanco.xml.bind.valueobject.BlancoXmlDocument;
import blanco.xml.bind.valueobject.BlancoXmlElement;

/**
 * OASIS XLIFF をシリアライズするクラス。
 * 
 * @author IGA Tosiki
 */
public class BlancoXliffSerializer {
    private BlancoXmlCharacters charNewLineWithIndent0 = null;

    private BlancoXmlCharacters charNewLineWithIndent2 = null;

    private BlancoXmlCharacters charNewLineWithIndent4 = null;

    private BlancoXmlCharacters charNewLineWithIndent6 = null;

    private BlancoXmlCharacters charNewLineWithIndent8 = null;

    private BlancoXmlCharacters charNewLineWithIndent10 = null;

    public BlancoXliffSerializer() {
        charNewLineWithIndent0 = new BlancoXmlCharacters();
        charNewLineWithIndent2 = new BlancoXmlCharacters();
        charNewLineWithIndent4 = new BlancoXmlCharacters();
        charNewLineWithIndent6 = new BlancoXmlCharacters();
        charNewLineWithIndent8 = new BlancoXmlCharacters();
        charNewLineWithIndent10 = new BlancoXmlCharacters();
        charNewLineWithIndent0.setValue("\n");
        charNewLineWithIndent2.setValue("\n  ");
        charNewLineWithIndent4.setValue("\n    ");
        charNewLineWithIndent6.setValue("\n      ");
        charNewLineWithIndent8.setValue("\n        ");
        charNewLineWithIndent10.setValue("\n          ");
    }

    /**
     * XLIFF情報をストリームに出力します。
     * 
     * @param argXliff
     * @param outStream
     */
    public void serialize(final BlancoXliff argXliff,
            final OutputStream outStream) {
        new BlancoXmlMarshaller().marshal(valueObject2Document(argXliff),
                outStream);
    }

    /**
     * XLIFF情報をファイルに出力します。
     * 
     * @param argXliff
     * @param argFileTarget
     */
    public void serialize(final BlancoXliff argXliff, final File argFileTarget) {
        new BlancoXmlMarshaller().marshal(valueObject2Document(argXliff),
                argFileTarget);
    }

    /**
     * 与えられたXLIFF情報から blancoXml DOMツリーを作成します。
     * 
     * @param argXliff
     * @return
     */
    public BlancoXmlDocument valueObject2Document(final BlancoXliff argXliff) {
        final BlancoXmlDocument document = new BlancoXmlDocument();

        final BlancoXmlElement eleXliff = new BlancoXmlElement();
        eleXliff.setQName("xliff");
        document.getChildNodes().add(eleXliff);

        {
            final BlancoXmlAttribute attr = new BlancoXmlAttribute();
            eleXliff.getAtts().add(attr);
            attr.setQName("version");
            attr.setValue(argXliff.getVersion());
        }

        for (int indexFileList = 0; indexFileList < argXliff.getFileList()
                .size(); indexFileList++) {
            BlancoXliffFile file = (BlancoXliffFile) argXliff.getFileList()
                    .get(indexFileList);
            BlancoXliffHeader header = file.getHeader();
            BlancoXliffBody body = file.getBody();

            eleXliff.getChildNodes().add(charNewLineWithIndent2);

            final BlancoXmlElement eleFile = new BlancoXmlElement();
            eleXliff.getChildNodes().add(eleFile);
            eleFile.setQName("file");

            {
                final BlancoXmlAttribute original = new BlancoXmlAttribute();
                eleFile.getAtts().add(original);
                original.setQName("original");
                original.setValue(file.getOriginal());

                final BlancoXmlAttribute sourceLanguage = new BlancoXmlAttribute();
                eleFile.getAtts().add(sourceLanguage);
                sourceLanguage.setQName("source-language");
                sourceLanguage.setValue(file.getSourceLanguage());

                final BlancoXmlAttribute targetLanguage = new BlancoXmlAttribute();
                eleFile.getAtts().add(targetLanguage);
                targetLanguage.setQName("target-language");
                targetLanguage.setValue(file.getTargetLanguage());

                final BlancoXmlAttribute dataType = new BlancoXmlAttribute();
                eleFile.getAtts().add(dataType);
                dataType.setQName("datatype");
                dataType.setValue(file.getDatatype());
            }

            eleFile.getChildNodes().add(charNewLineWithIndent4);

            final BlancoXmlElement eleHeader = new BlancoXmlElement();
            eleFile.getChildNodes().add(eleHeader);
            eleHeader.setQName("header");

            for (int indexTool = 0; indexTool < header.getToolList().size(); indexTool++) {
                final BlancoXliffTool tool = (BlancoXliffTool) header
                        .getToolList().get(indexTool);

                final BlancoXmlElement eleTool = new BlancoXmlElement();
                eleHeader.getChildNodes().add(charNewLineWithIndent6);
                eleHeader.getChildNodes().add(eleTool);
                eleTool.setQName("tool");

                {
                    final BlancoXmlAttribute attr = new BlancoXmlAttribute();
                    eleTool.getAtts().add(attr);
                    attr.setQName("tool-id");
                    attr.setValue(tool.getToolId());
                }
                {
                    final BlancoXmlAttribute attr = new BlancoXmlAttribute();
                    eleTool.getAtts().add(attr);
                    attr.setQName("tool-name");
                    attr.setValue(tool.getToolName());
                }
            }

            eleHeader.getChildNodes().add(charNewLineWithIndent4);
            eleFile.getChildNodes().add(charNewLineWithIndent4);

            final BlancoXmlElement eleBody = new BlancoXmlElement();
            eleFile.getChildNodes().add(eleBody);
            eleBody.setQName("body");

            for (int index = 0; index < body.getTransUnitList().size(); index++) {
                final BlancoXliffTransUnit transUnit = (BlancoXliffTransUnit) body
                        .getTransUnitList().get(index);

                eleBody.getChildNodes().add(charNewLineWithIndent6);

                final BlancoXmlElement eleTranUnit = new BlancoXmlElement();
                eleBody.getChildNodes().add(eleTranUnit);
                eleTranUnit.setQName("trans-unit");

                eleTranUnit.getChildNodes().add(charNewLineWithIndent8);

                {
                    final BlancoXmlAttribute attr = new BlancoXmlAttribute();
                    eleTranUnit.getAtts().add(attr);
                    attr.setQName("id");
                    attr.setValue(transUnit.getId());
                }

                if (transUnit.getApproved()) {
                    // trueの場合にのみセット。falseの場合には何もしません。
                    final BlancoXmlAttribute attr = new BlancoXmlAttribute();
                    eleTranUnit.getAtts().add(attr);
                    attr.setQName("approved");
                    attr.setValue("yes");
                }

                if (transUnit.getTranslate() == false) {
                    // falseの場合にのみセット。trueの場合には何もしません。
                    final BlancoXmlAttribute attr = new BlancoXmlAttribute();
                    eleTranUnit.getAtts().add(attr);
                    attr.setQName("translate");
                    attr.setValue("no");
                }

                final BlancoXmlElement eleSource = new BlancoXmlElement();
                eleSource.setQName("source");
                eleTranUnit.getChildNodes().add(eleSource);

                {
                    final BlancoXmlCharacters xmlString = new BlancoXmlCharacters();
                    xmlString.setValue(transUnit.getSource());
                    eleSource.getChildNodes().add(xmlString);
                }

                final BlancoXliffTarget transUnitTarget = (BlancoXliffTarget) transUnit
                        .getTarget();
                if (transUnitTarget != null) {
                    eleTranUnit.getChildNodes().add(charNewLineWithIndent8);

                    final BlancoXmlElement eleTarget = new BlancoXmlElement();
                    eleTarget.setQName("target");
                    eleTranUnit.getChildNodes().add(eleTarget);

                    if (BlancoStringUtil.null2Blank(transUnitTarget.getState())
                            .length() > 0) {
                        final BlancoXmlAttribute attr = new BlancoXmlAttribute();
                        eleTarget.getAtts().add(attr);
                        attr.setQName("state");
                        attr.setValue(transUnitTarget.getState());
                    }

                    if (BlancoStringUtil.null2Blank(
                            transUnitTarget.getStateQualifier()).length() > 0) {
                        final BlancoXmlAttribute attr = new BlancoXmlAttribute();
                        eleTarget.getAtts().add(attr);
                        attr.setQName("state-qualifier");
                        attr.setValue(transUnitTarget.getStateQualifier());
                    }

                    {
                        final BlancoXmlCharacters xmlString = new BlancoXmlCharacters();
                        xmlString.setValue(transUnitTarget.getTarget());
                        eleTarget.getChildNodes().add(xmlString);
                    }
                }

                for (int indexNote = 0; indexNote < transUnit.getNoteList()
                        .size(); indexNote++) {
                    final BlancoXliffNote note = (BlancoXliffNote) transUnit
                            .getNoteList().get(indexNote);
                    eleTranUnit.getChildNodes().add(charNewLineWithIndent8);
                    expandNote(note, eleTranUnit);
                }

                for (int indexAltTrans = 0; indexAltTrans < transUnit
                        .getAltTransList().size(); indexAltTrans++) {
                    final BlancoXliffAltTrans altTrans = (BlancoXliffAltTrans) transUnit
                            .getAltTransList().get(indexAltTrans);

                    eleTranUnit.getChildNodes().add(charNewLineWithIndent8);

                    final BlancoXmlElement eleAltTrans = new BlancoXmlElement();
                    eleAltTrans.setQName("alt-trans");
                    eleTranUnit.getChildNodes().add(eleAltTrans);

                    if (BlancoStringUtil.null2Blank(altTrans.getToolId())
                            .length() > 0) {
                        final BlancoXmlAttribute attr = new BlancoXmlAttribute();
                        eleAltTrans.getAtts().add(attr);
                        attr.setQName("tool-id");
                        attr.setValue(altTrans.getToolId());
                    }

                    final BlancoXmlElement eleSourceAltTrans = new BlancoXmlElement();
                    eleSourceAltTrans.setQName("source");

                    {
                        final BlancoXmlCharacters xmlString = new BlancoXmlCharacters();
                        xmlString.setValue(altTrans.getSource());
                        eleSourceAltTrans.getChildNodes().add(xmlString);
                    }

                    eleAltTrans.getChildNodes().add(charNewLineWithIndent10);
                    eleAltTrans.getChildNodes().add(eleSourceAltTrans);

                    final BlancoXmlElement eleTarget = new BlancoXmlElement();
                    eleTarget.setQName("target");
                    eleAltTrans.getChildNodes().add(charNewLineWithIndent10);
                    eleAltTrans.getChildNodes().add(eleTarget);

                    if (BlancoStringUtil.null2Blank(altTrans.getLang())
                            .length() > 0) {
                        final BlancoXmlAttribute attr = new BlancoXmlAttribute();
                        eleTarget.getAtts().add(attr);
                        attr.setQName("xml:lang");
                        attr.setValue(altTrans.getLang());
                    }

                    {
                        final BlancoXmlCharacters xmlString = new BlancoXmlCharacters();
                        xmlString.setValue(altTrans.getTarget().getTarget());
                        eleTarget.getChildNodes().add(xmlString);
                    }

                    for (int indexNote = 0; indexNote < altTrans.getNoteList()
                            .size(); indexNote++) {
                        final BlancoXliffNote note = (BlancoXliffNote) altTrans
                                .getNoteList().get(indexNote);
                        eleAltTrans.getChildNodes()
                                .add(charNewLineWithIndent10);
                        expandNote(note, eleAltTrans);
                    }

                    eleAltTrans.getChildNodes().add(charNewLineWithIndent8);
                }

                eleTranUnit.getChildNodes().add(charNewLineWithIndent6);
            }

            eleBody.getChildNodes().add(charNewLineWithIndent4);
            eleFile.getChildNodes().add(charNewLineWithIndent2);
        }
        eleXliff.getChildNodes().add(charNewLineWithIndent0);

        return document;
    }

    /**
     * noteノードを展開します。
     * 
     * @param note
     * @param targetElement
     */
    private void expandNote(final BlancoXliffNote note,
            final BlancoXmlElement targetElement) {
        final BlancoXmlCharacters charNewLineWithIndent8 = new BlancoXmlCharacters();

        final BlancoXmlElement eleNote = new BlancoXmlElement();
        targetElement.getChildNodes().add(charNewLineWithIndent8);
        targetElement.getChildNodes().add(eleNote);
        eleNote.setQName("note");

        if (BlancoStringUtil.null2Blank(note.getLang()).length() > 0) {
            final BlancoXmlAttribute attr = new BlancoXmlAttribute();
            eleNote.getAtts().add(attr);
            attr.setQName("xml:lang");
            attr.setValue(note.getLang());
        }
        if (BlancoStringUtil.null2Blank(note.getFrom()).length() > 0) {
            final BlancoXmlAttribute attr = new BlancoXmlAttribute();
            eleNote.getAtts().add(attr);
            attr.setQName("from");
            attr.setValue(note.getFrom());
        }

        {
            final BlancoXmlCharacters xmlString = new BlancoXmlCharacters();
            xmlString.setValue(note.getText());
            eleNote.getChildNodes().add(xmlString);
        }
    }
}
