/*
 * Copyright (c) 2009 The openGion Project.
 *
 * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
 *
 * 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 org.opengion.fukurou.xml;

import org.xml.sax.Attributes;
import java.util.Set;
import java.util.HashSet;
import java.util.List;
// import java.util.ArrayList;

/**
 * エレメントをあらわす、OGElement クラスを定義します。
 *
 * エレメントは、OGNode クラスを継承し、名称、属性、ノードリストを持つオブジェクトです。
 * 通常で言うところの、タグになります。
 * 属性は、OGAttributes クラスで管理します。ノードリスト に関する操作は、OGNodeクラスの実装です。
 *
 * OGNode は、enum OGNodeType で区別される状態を持っています。
 * OGNodeType は、それぞれ、再設定が可能です。
 * 例えば、既存のエレメントやノードに対して、コメントタイプ(Comment)を指定すると、
 * ファイル等への出力時にコメントとして出力されます。
 *
 * @og.rev 5.1.8.0 (2010/07/01) 新規作成
 *
 * @version  5.0
 * @author   Kazuhiko Hasegawa
 * @since    JDK6.0,
 */
public class OGElement extends OGNode {

	/** 特殊：内部的に、タグ属性の改行処理を行うタグ名の Map(Set)を持っています。 **/
	private static final Set<String> CR_SET = new HashSet<String>();
	// 初期処理
	static {
		CR_SET.add( "og:comment" );
		CR_SET.add( "og:view" );
	//	CR_SET.add( "og:link" );
	}

	private final String		qName ;		// このタグの名前
	private final OGAttributes	attri;		// 属性オブジェクト

	/**
	 * ノード名を指定してのトコンストラクター
	 *
	 * ノード名のみ指定するため、属性と、ノードリストが空のエレメントを構築します。
	 *
	 * @param qName String ノード名
	 */
	public OGElement( final String qName ) {
		this( qName,null,null );
	}

	/**
	 * ノード名、属性タブ、属性リストを指定してのトコンストラクター
	 *
	 * 注意 属性値の正規化は必ず行われます。
	 * 属性値に含まれるCR(復帰), LF(改行), TAB(タブ)は、 半角スペースに置き換えられます。
	 * XMLの規定では、属性の並び順は保障されませんが、SAXのAttributesは、XMLに記述された順番で
	 * 取得できていますので、このクラスでの属性リストも、記述順での並び順になります。
	 * 
	 * @param qName String ノード名
	 * @param attTab String 属性タブ
	 * @param atts Attributes 属性リスト
	 */
	public OGElement( final String qName , final String attTab , final Attributes atts ) {
		super();
		setNodeType( OGNodeType.Element );

		if( qName == null ) {
			String errMsg = "エレメントには、ノード名は必須です。";
			throw new RuntimeException( errMsg );
		}

		this.qName    = qName;
		boolean useCR = CR_SET.contains( qName );
		this.attri    = new OGAttributes( attTab , atts , useCR ) ;
	}

	/**
	 * ノード名を返します。
	 * 
	 * @return String ノード名
	 */
	public String getTagName() {
		return qName;
	}

	/**
	 * 属性リストの個数を返します。
	 * 
	 * @return int 属性リストの個数
	 */
	public int getAttSize() {
		return attri.size();
	}

	/**
	 * 指定のアドレスの属性リストのキーを返します。
	 * 
	 * @param adrs int 属性リストのアドレス
	 * @return String 属性リストのキー
	 */
	public String getKey( final int adrs ) {
		return attri.getKey( adrs );
	}

	/**
	 * 指定のアドレスの属性リストの値を返します。
	 * 
	 * @param adrs int 属性リストのアドレス
	 * @return String 属性リストの値
	 */
	public String getVal( final int adrs ) {
		return attri.getVal( adrs );
	}

	/**
	 * 指定のアドレスの属性リストの値を設定します。
	 * 
	 * これは、キー指定ではなく、アドレス指定で、属性値の書き換えを行います。
	 *
	 * @param adrs int 属性リストのアドレス
	 * @param val String 属性リストの値
	 */
	public void setVal( final int adrs , final String val ) {
		attri.setVal( adrs,val ) ;
	}

	/**
	 * 属性リストに、属性(キー、値のセット）を設定します。
	 * 
	 * 属性リストの一番最後に、属性(キー、値のセット）を設定します。
	 *
	 * @param key String 属性リストのキー
	 * @param val String 属性リストの値
	 */
	public void addAttr( final String key , final String val ) {
		attri.add( key,val ) ;
	}

	/**
	 * 指定のアドレスの属性リストに、属性(キー、値のセット）を設定します。
	 * 
	 * 指定のアドレスの属性を置き換えるのではなく追加します。
	 *
	 * @param adrs int 属性リストのアドレス
	 * @param key String 属性リストのキー
	 * @param val String 属性リストの値
	 */
	public void addAttr( final int adrs , final String key , final String val ) {
		attri.add( adrs,key,val ) ;
	}

	/**
	 * 指定のアドレスの属性リストから、属性情報を削除します。
	 * 
	 * 指定のアドレスの属性を置き換えるのではなく追加します。
	 *
	 * @param adrs int 属性リストのアドレス
	 */
	public void removeAttr( final int adrs ) {
		attri.remove( adrs ) ;
	}

	/**
	 * オブジェクトの文字列表現を返します。
	 *
	 * 文字列は、OGNodeType により異なります。
	 * Comment ノードの場合は、コメント記号を、Cdata ノードの場合は、CDATA を
	 * つけて出力します。
	 *
	 * @return String このオブジェクトの文字列表現
	 * @see OGNode#toString()
	 */
	public String toString() {
		StringBuilder buf = new StringBuilder();

		buf.append( "<" ).append( qName );

		buf.append( attri.toString() );

		String text = getText();

		if( text.length() > 0 ) {
			buf.append( ">" ).append( text ).append( "</" ).append( qName ).append( ">" );
		}
		else {
			buf.append( " />" );
		}

		final String rtn ;
		switch( getNodeType() ) {
			case Comment:	rtn = "<!--"      + buf.toString() + "-->"; break;
			case Cdata:		rtn = "<![CDATA[" + buf.toString() + "]]>"; break;
			case Text:
			case List:
			default:		rtn = buf.toString(); break;
		}

		return rtn ;
	}
}
