/*
 * 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.util.ArrayList;
import java.util.List;

import blanco.xml.bind.valueobject.BlancoXmlAttribute;
import blanco.xml.bind.valueobject.BlancoXmlCharacters;
import blanco.xml.bind.valueobject.BlancoXmlDocument;
import blanco.xml.bind.valueobject.BlancoXmlElement;

/**
 * blancoXmlBindingで得られたバリューオブジェクトに対する操作支援ユーティリティです。
 * 
 * @author IGA Tosiki
 */
public class BlancoXmlBindingUtil {
    /**
     * このクラスはインスタンス化はしません。
     */
    private BlancoXmlBindingUtil() {
    }

    /**
     * 与えられたドキュメントからエレメントを取得します。
     * 
     * 最初に見つかったエレメントを戻します。
     * 
     * @param argDocument
     *            検索対象となるドキュメント。
     * @return (最初に見つかった)エレメント。エレメントが見つからなかった場合にはnullが戻ります。
     */
    public static final BlancoXmlElement getDocumentElement(
            final BlancoXmlDocument argDocument) {
        final ArrayList listRoot = argDocument.getChildNodes();
        for (int index = 0; index < listRoot.size(); index++) {
            final Object objLook = listRoot.get(index);
            if (objLook instanceof BlancoXmlElement == false) {
                continue;
            }
            return (BlancoXmlElement) objLook;
        }

        // ひとつも見つかりませんでした。
        return null;
    }

    /**
     * 与えられたエレメントの子ノードからエレメントを取得します。
     * 
     * 検索に合致した全てのエレメントを戻します。
     * 
     * @param argElement
     *            検索対象となるエレメント。
     * @param argTagname
     *            検索したいタグ名。(ローカル名)
     * @return 見つかったエレメントのリスト。エレメントが見つからなかった場合には空のListが戻ります。
     */
    public static final List getElementsByTagName(
            final BlancoXmlElement argElement, final String argTagname) {
        if (argElement == null) {
            throw new IllegalArgumentException(
                    "BlancoXmlBindingUtil.getElementsByTagName: 引数(エレメント)にnullが与えられました。");
        }

        final ArrayList listResult = new ArrayList();

        final ArrayList listChild = argElement.getChildNodes();
        for (int index = 0; index < listChild.size(); index++) {
            final Object objLook = listChild.get(index);
            if (objLook instanceof BlancoXmlElement == false) {
                continue;
            }

            final BlancoXmlElement elementLook = (BlancoXmlElement) objLook;
            if (elementLook.getLocalName().equals(argTagname)) {
                listResult.add((BlancoXmlElement) objLook);
            }
        }

        return listResult;
    }

    /**
     * 与えられたエレメントの子ノードからエレメントを取得します。
     * 
     * 最初に見つかったエレメントを戻します。複数のエレメントが合致したとしても２つ目以降は無視されます。
     * 
     * @param argElement
     *            検索対象となるエレメント。
     * @param argTagname
     *            検索したいタグ名。(ローカル名)
     * @return 最初に見つかったエレメント。エレメントが見つからなかった場合にはnullが戻ります。
     */
    public static final BlancoXmlElement getElement(
            final BlancoXmlElement argElement, final String argTagname) {
        final ArrayList listChild = argElement.getChildNodes();
        for (int index = 0; index < listChild.size(); index++) {
            final Object objLook = listChild.get(index);
            if (objLook instanceof BlancoXmlElement == false) {
                continue;
            }

            final BlancoXmlElement elementLook = (BlancoXmlElement) objLook;
            if (elementLook.getLocalName().equals(argTagname)) {
                return elementLook;
            }
        }

        // ひとつも見つかりませんでした。
        return null;
    }

    /**
     * 選択されたノード(エレメントでも可)から文字列を取得します。
     * 
     * あるノードにぶらさがっている全てのテキストデータを取得する場合に利用します。
     * 
     * @param argElement
     *            対象とするターゲットエレメント
     * @return 取得されたテキスト文字列。取得されなかった場合にはnullが戻ります。
     */
    public static final String getTextContent(final BlancoXmlElement argElement) {
        if (argElement == null) {
            throw new IllegalArgumentException(
                    "ノードからテキストを取得するメソッドにnullが与えられました。null以外の値を与えるようにしてください。");
        }

        final StringBuffer result = new StringBuffer();
        boolean isProcessed = false;

        final ArrayList listText = argElement.getChildNodes();
        final int sizeChildList = listText.size();
        for (int indexChild = 0; indexChild < sizeChildList; indexChild++) {
            final Object objLook = listText.get(indexChild);
            if (objLook instanceof BlancoXmlCharacters) {
                final BlancoXmlCharacters textLook = (BlancoXmlCharacters) objLook;
                result.append(textLook.getValue());
                isProcessed = true;
            }
        }

        if (isProcessed == false) {
            return null;
        } else {
            return result.toString();
        }
    }

    /**
     * エレメントから指定のタグ名の文字列を読み込みます。
     * 
     * @param elementTarget
     *            対象とするターゲットエレメント
     * @param tagName
     *            タグ名
     * @return 取得されたテキスト文字列。取得されなかった場合にはnullが戻ります。
     */
    public static final String getTextContent(
            final BlancoXmlElement elementTarget, final String tagName) {
        if (elementTarget == null) {
            throw new IllegalArgumentException(
                    "エレメントからテキストを取得するメソッドにエレメントとしてnullが与えられました。null以外の値を与えるようにしてください。");
        }
        if (tagName == null) {
            throw new IllegalArgumentException(
                    "エレメントからテキストを取得するメソッドにタグ名としてnullが与えられました。null以外の値を与えるようにしてください。");
        }

        final StringBuffer result = new StringBuffer();
        boolean isProcessed = false;

        final List listElementTarget = getElementsByTagName(elementTarget,
                tagName);
        final int sizeList = listElementTarget.size();
        for (int index = 0; index < sizeList; index++) {
            final Object nodeLook = listElementTarget.get(index);
            if (nodeLook instanceof BlancoXmlElement) {
                final BlancoXmlElement elementLook = (BlancoXmlElement) nodeLook;

                final List listText = elementLook.getChildNodes();
                final int sizeChildList = listText.size();
                for (int indexChild = 0; indexChild < sizeChildList; indexChild++) {
                    final Object nodeChild = listText.get(indexChild);
                    if (nodeChild instanceof BlancoXmlCharacters) {
                        final BlancoXmlCharacters textLook = (BlancoXmlCharacters) nodeChild;
                        result.append(textLook.getValue());
                        isProcessed = true;
                    }
                }
            }
        }

        if (isProcessed == false) {
            return null;
        } else {
            return result.toString();
        }
    }

    /**
     * エレメントから与えられたQNameによるアトリビュート値を取得します。
     * 
     * @param argElement
     * @param argQName
     * @return
     */
    public static final String getAttribute(final BlancoXmlElement argElement,
            final String argQName) {
        for (int indexAttr = 0; indexAttr < argElement.getAtts().size(); indexAttr++) {
            final Object objLook = argElement.getAtts().get(indexAttr);
            if (objLook instanceof BlancoXmlAttribute) {
                final BlancoXmlAttribute attr = (BlancoXmlAttribute) argElement
                        .getAtts().get(indexAttr);
                if (argQName.equals(attr.getQName())) {
                    return attr.getValue();
                }
            }
        }

        // 発見できませんでした。
        return null;
    }
}
