package com.nifty.cloud.ncs.util;

import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;

/**
 * XML出力用クラスです。
 *
 */
public class XmlWriter
{
	private static final Charset UTF8 = Charset.forName("UTF-8");
	private List<String> tags = new ArrayList<String>();
	private StringBuilder sb = new StringBuilder();

	/**
	 * XMLの開始タグを追加します。
	 *
	 * @param name
	 *   タグの文字列
	 * @return
	 *   自オブジェクトのインスタンス
	 */
	public XmlWriter start(String name)
	{
		sb.append("<").append(name).append(">");
		tags.add(name);
		return (this);
	}

	/**
	 * XMLの開始タグを追加します。
	 *
	 * @param name
	 *   タグの文字列
	 * @param attr
	 *   属性の文字列
	 * @param value
	 *   値の文字列
	 * @return
	 *   自オブジェクトのインスタンス
	 */
	public XmlWriter start(String name, String attr, String value)
	{
		sb.append("<").append(name);
		writeAttr(attr, value);
		sb.append(">");
		tags.add(name);
		return (this);
	}

	/**
	 * XMLの開始タグを追加します。
	 *
	 * @param name
	 *   タグの文字列
	 * @param attrs
	 *   属性の文字列の配列
	 * @param values
	 *   値の文字列の配列
	 * @return
	 *   自オブジェクトのインスタンス
	 */
	public XmlWriter start(String name, String[] attrs, String[] values)
	{
		sb.append("<").append(name);

		for (int i=0; i<Math.min(attrs.length, values.length); i++) {
			writeAttr(attrs[i], values[i]);
		}
		sb.append(">");
		tags.add(name);
		return (this);
	}

	/**
	 * XMLの終了タグを追加します。
	 *
	 * @return
	 *   自オブジェクトのインスタンス
	 */
	public XmlWriter end()
	{
		assert(tags.size() > 0);
		String name = tags.remove(tags.size()-1);
		sb.append("</").append(name).append(">");
		return (this);
	}

	/**
	 * XMLデータのバイトデータを取得します。
	 *
	 * @return
	 *   XMLデータのバイトデータ
	 */
	public byte[] getBytes()
	{
		assert(tags.size() == 0);
		return (this.toString().getBytes(UTF8));
	}

	/**
	 * XMLデータの文字列を取得します。
	 *
	 * @return
	 *   XMLデータの文字列
	 */
	public String toString()
	{
		return (sb.toString());
	}

	/**
	 * XMLに値を追加します。
	 *
	 * @param value
	 *   値の文字列
	 * @return
	 *   自オブジェクトのインスタンス
	 */
	public XmlWriter value(String value)
	{
		appendEscapedString(value);
		return (this);
	}

	/**
	 * XMLに属性を追加します。
	 *
	 * @param name
	 *   属性の文字列
	 * @param value
	 *   値の文字列
	 */
	private void writeAttr(String name, String value)
	{
		sb.append(' ').append(name).append("=\"");
		appendEscapedString(value);
		sb.append("\"");
	}

	/**
	 * 文字列をエスケープします。
	 *
	 * @param s
	 *   文字列
	 */
	private void appendEscapedString(String s)
	{
		if (s == null) {
			s = "";
		}
		int pos;
		int start = 0;
		int len = s.length();

		for (pos=0; pos<len; pos++) {
			char ch = s.charAt(pos);
			String escape;

			switch(ch) {
			case '\t':
				escape = "&#9;";
				break;
			case '\n':
				escape = "&#10;";
				break;
			case '\r':
				escape = "&#13;";
				break;
			case '&':
				escape = "&amp;";
				break;
			case '"':
				escape = "&quot;";
				break;
			case '<':
				escape = "&lt;";
				break;
			case '>':
				escape = "&gt;";
				break;
			default:
				escape = null;
				break;
			}

			if (escape != null) {
				if (start < pos) {
					sb.append(s, start, pos);
				}
				sb.append(escape);
				start = pos + 1;
			}
		}

		if (start < pos) {
			sb.append(s, start, pos);
		}
	}
}
