/*
 * Copyright (c) 2003, Morpho Project.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *	  this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *	  this list of conditions and the following disclaimer in the documentation
 *	  and/or other materials provided with the distribution.
 * 3. Neither the name of the Morpho Project nor the names of its contributors
 *	  may be used to endorse or promote products derived from this software
 *	  without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
package jp.morpho.webapp.servlet.jsp.tagext.html;

import java.io.IOException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;

import jp.morpho.util.ConversionMap;
import jp.morpho.webapp.servlet.http.HttpActionRequest;
import jp.morpho.webapp.servlet.http.impl.HttpActionRequestImpl;
import jp.morpho.webapp.servlet.jsp.tagext.TagManager;
import jp.morpho.webapp.servlet.jsp.tagext.TagTemplate;

/**
 * <P>
 * HTML の INPUT 要素を実装したタグハンドラです。
 * テキスト入力、チェックボックス、ラジオボタンなどのコントロールに適したタグハンドラを作成するために、サブクラス化を前提とする abstract クラスを提供します。
 * </P>
 * <P>
 * INPUT の HTML 4 定義は次のとおりです。
 * <PRE>
 * &lt;!ENTITY % InputType
 *   "(TEXT | PASSWORD | CHECKBOX |
 *     RADIO | SUBMIT | RESET |
 *     FILE | HIDDEN | IMAGE | BUTTON)"
 *    &gt;
 * 
 * &lt;!-- attribute name required for all but submit and reset --&gt;
 * &lt;!ELEMENT INPUT - O EMPTY              -- form control --&gt;
 * &lt;!ATTLIST INPUT
 *   %attrs;                              -- %coreattrs, %i18n, %events --
 *   type        %InputType;    TEXT      -- what kind of widget is needed --
 *   name        CDATA          #IMPLIED  -- submit as part of form --
 *   value       CDATA          #IMPLIED  -- Specify for radio buttons and checkboxes --
 *   checked     (checked)      #IMPLIED  -- for radio buttons and check boxes --
 *   disabled    (disabled)     #IMPLIED  -- unavailable in this context --
 *   readonly    (readonly)     #IMPLIED  -- for text and passwd --
 *   size        CDATA          #IMPLIED  -- specific to each type of field --
 *   maxlength   NUMBER         #IMPLIED  -- max chars for text fields --
 *   src         %URI;          #IMPLIED  -- for fields with images --
 *   alt         CDATA          #IMPLIED  -- short description --
 *   usemap      %URI;          #IMPLIED  -- use client-side image map --
 *   ismap       (ismap)        #IMPLIED  -- use server-side image map --
 *   tabindex    NUMBER         #IMPLIED  -- position in tabbing order --
 *   accesskey   %Character;    #IMPLIED  -- accessibility key character --
 *   onfocus     %Script;       #IMPLIED  -- the element got the focus --
 *   onblur      %Script;       #IMPLIED  -- the element lost the focus --
 *   onselect    %Script;       #IMPLIED  -- some text was selected --
 *   onchange    %Script;       #IMPLIED  -- the element value was changed --
 *   accept      %ContentTypes; #IMPLIED  -- list of MIME types for file upload --
 *   &gt;
 * </PRE>
 * </P>
 * @author Kumiko Hiroi
 */
public abstract class InputTag
	extends ControlTagSupport
	implements TagTemplate
{

	/**
	 * <P>
	 * InputTag を作成します。
	 * </P>
	 */
	public InputTag()
	{
		super();
	}

	/**
	 * <P>
	 * accept 属性を返します。
	 * </P>
	 * @return accept 属性
	 */
	public String getAccept()
	{
		return getAttributeMap().getString("accept");
	}

	/**
	 * <P>
	 * accesskey 属性を返します。
	 * </P>
	 * @return accesskey 属性
	 */
	public String getAccesskey()
	{
		return getAttributeMap().getString("accesskey");
	}

	/**
	 * <P>
	 * alt 属性を返します。
	 * </P>
	 * @return alt 属性
	 */
	public String getAlt()
	{
		return getAttributeMap().getString("alt");
	}

	/**
	 * <P>
	 * checked 属性を返します。
	 * </P>
	 * @return checked 属性
	 */
	public boolean getChecked()
	{
		return getAttributeMap().getBoolean("checked");
	}

	/**
	 * <P>
	 * disabled 属性を返します。
	 * </P>
	 * @return disabled 属性
	 */
	public boolean getDisabled()
	{
		return getAttributeMap().getBoolean("disabled");
	}

	/**
	 * <P>
	 * ismap 属性を返します。
	 * </P>
	 * @return ismap 属性
	 */
	public boolean getIsmap()
	{
		return getAttributeMap().getBoolean("ismap");
	}

	/**
	 * <P>
	 * maxlength 属性を返します。
	 * </P>
	 * @return maxlength 属性
	 */
	public String getMaxlength()
	{
		return getAttributeMap().getString("maxlength");
	}

	/**
	 * <P>
	 * name 属性を返します。
	 * </P>
	 * @return name 属性
	 */
	public String getName()
	{
		return getAttributeMap().getString("name");
	}

	/**
	 * <P>
	 * onblur 属性を返します。
	 * </P>
	 * @return onblur 属性
	 */
	public String getOnblur()
	{
		return getAttributeMap().getString("onblur");
	}

	/**
	 * <P>
	 * onchange 属性を返します。
	 * </P>
	 * @return onchange 属性
	 */
	public String getOnchange()
	{
		return getAttributeMap().getString("onchange");
	}

	/**
	 * <P>
	 * onfocus 属性を返します。
	 * </P>
	 * @return onfocus 属性
	 */
	public String getOnfocus()
	{
		return getAttributeMap().getString("onfocus");
	}

	/**
	 * <P>
	 * onselect 属性を返します。
	 * </P>
	 * @return onselect 属性
	 */
	public String getOnselect()
	{
		return getAttributeMap().getString("onselect");
	}

	/**
	 * <P>
	 * readonly 属性を返します。
	 * </P>
	 * @return readonly 属性
	 */
	public boolean getReadonly()
	{
		return getAttributeMap().getBoolean("readonly");
	}

	/**
	 * <P>
	 * size 属性を返します。
	 * </P>
	 * @return size 属性
	 */
	public String getSize()
	{
		return getAttributeMap().getString("size");
	}

	/**
	 * <P>
	 * src 属性を返します。
	 * </P>
	 * @return src 属性
	 */
	public String getSrc()
	{
		return getAttributeMap().getString("src");
	}

	/**
	 * <P>
	 * tabindex 属性を返します。
	 * </P>
	 * @return tabindex 属性
	 */
	public String getTabindex()
	{
		return getAttributeMap().getString("tabindex");
	}

	/**
	 * <P>
	 * type 属性を返します。
	 * </P>
	 * @return type 属性
	 */
	public String getType()
	{
		return getAttributeMap().getString("type");
	}

	/**
	 * <P>
	 * usemap 属性を返します。
	 * </P>
	 * @return usemap 属性
	 */
	public String getUsemap()
	{
		return getAttributeMap().getString("usemap");
	}

	/**
	 * <P>
	 * value 属性を返します。
	 * </P>
	 * @return value 属性
	 */
	public String getValue()
	{
		return getAttributeMap().getString("value");
	}

	/**
	 * <P>
	 * template 属性を返します。
	 * </P>
	 * @return template 属性
	 */
	public String getTemplate()
	{
		return getAttributeMap().getString("template");
	}

	/**
	 * <P>
	 * accept 属性を設定します。
	 * </P>
	 * @param accept accept 属性
	 */
	public void setAccept(String accept)
	{
		getAttributeMap().putString("accept", accept);
	}

	/**
	 * <P>
	 * accesskey 属性を設定します。
	 * </P>
	 * @param accesskey accesskey 属性
	 */
	public void setAccesskey(String accesskey)
	{
		getAttributeMap().putString("accesskey", accesskey);
	}

	/**
	 * <P>
	 * alt 属性を設定します。
	 * </P>
	 * @param alt alt 属性
	 */
	public void setAlt(String alt)
	{
		getAttributeMap().putString("alt", alt);
	}

	/**
	 * <P>
	 * checked 属性を設定します。
	 * </P>
	 * @param checked checked 属性
	 */
	public void setChecked(boolean checked)
	{
		getAttributeMap().putBoolean("checked", checked);
	}

	/**
	 * <P>
	 * disabled 属性を設定します。
	 * </P>
	 * @param disabled disabled 属性
	 */
	public void setDisabled(boolean disabled)
	{
		getAttributeMap().putBoolean("disabled", disabled);
	}

	/**
	 * <P>
	 * ismap 属性を設定します。
	 * </P>
	 * @param ismap ismap 属性
	 */
	public void setIsmap(boolean ismap)
	{
		getAttributeMap().putBoolean("ismap", ismap);
	}

	/**
	 * <P>
	 * maxlength 属性を設定します。
	 * </P>
	 * @param maxlength maxlength 属性
	 */
	public void setMaxlength(String maxlength)
	{
		getAttributeMap().putString("maxlength", maxlength);
	}

	/**
	 * <P>
	 * name 属性を設定します。
	 * </P>
	 * @param name name 属性
	 */
	public void setName(String name)
	{
		getAttributeMap().putString("name", name);
	}

	/**
	 * <P>
	 * onblur 属性を設定します。
	 * </P>
	 * @param onblur onblur 属性
	 */
	public void setOnblur(String onblur)
	{
		getAttributeMap().putString("onblur", onblur);
	}

	/**
	 * <P>
	 * onchange 属性を設定します。
	 * </P>
	 * @param onchange onchange 属性
	 */
	public void setOnchange(String onchange)
	{
		getAttributeMap().putString("onchange", onchange);
	}

	/**
	 * <P>
	 * onfocus 属性を設定します。
	 * </P>
	 * @param onfocus onfocus 属性
	 */
	public void setOnfocus(String onfocus)
	{
		getAttributeMap().putString("onfocus", onfocus);
	}

	/**
	 * <P>
	 * onselect 属性を設定します。
	 * </P>
	 * @param onselect onselect 属性
	 */
	public void setOnselect(String onselect)
	{
		getAttributeMap().putString("onselect", onselect);
	}

	/**
	 * <P>
	 * readonly 属性を設定します。
	 * </P>
	 * @param readonly readonly 属性
	 */
	public void setReadonly(boolean readonly)
	{
		getAttributeMap().putBoolean("readonly", readonly);
	}

	/**
	 * <P>
	 * size 属性を設定します。
	 * </P>
	 * @param size size 属性
	 */
	public void setSize(String size)
	{
		getAttributeMap().putString("size", size);
	}

	/**
	 * <P>
	 * src 属性を設定します。
	 * </P>
	 * @param src src 属性
	 */
	public void setSrc(String src)
	{
		getAttributeMap().putString("src", src);
	}

	/**
	 * <P>
	 * tabindex 属性を設定します。
	 * </P>
	 * @param tabindex tabindex 属性
	 */
	public void setTabindex(String tabindex)
	{
		getAttributeMap().putString("tabindex", tabindex);
	}

	/**
	 * <P>
	 * type 属性を設定します。
	 * </P>
	 * @param type type 属性
	 */
	public void setType(String type)
	{
		getAttributeMap().putString("type", type);
	}

	/**
	 * <P>
	 * usemap 属性を設定します。
	 * </P>
	 * @param usemap usemap 属性
	 */
	public void setUsemap(String usemap)
	{
		getAttributeMap().putString("usemap", usemap);
	}

	/**
	 * <P>
	 * value 属性を設定します。
	 * </P>
	 * @param value value 属性
	 */
	public void setValue(String value)
	{
		getAttributeMap().putString("value", value);
	}

	/**
	 * <P>
	 * template 属性を設定します。
	 * </P>
	 * @param template template 属性
	 */
	public void setTemplate(String template)
	{
		getAttributeMap().putString("template", template);
	}

	/**
	 * <P>
	 * 開始タグの処理です。
	 * </P>
	 * @return EVAL_BODY_BUFFERED
	 * @see javax.servlet.jsp.tagext.Tag#doStartTag()
	 */
	public int doStartTag() throws JspException
	{
		//INPUTタグを作成します
		StringBuffer buf = new StringBuffer();
		buf.append("<input");

		//TagTemplateを取得します
		TagManager tm = this.getTagManager();
		TagTemplate ttmp = null;
		if (this.getAttributeMap().containsKey("template"))
		{
			ttmp = tm.getTagTemplate(this.getTagType(), this.getTemplate());
		}
		else
		{
			ttmp = tm.getTagTemplate(this.getTagType(), this.getName());
		}

		ConversionMap attr = new ConversionMap();
		if (ttmp != null)
		{
			attr.putAll(ttmp.getAttributeMap());
		}
		attr.putAll(this.getAttributeMap());

		String keys[] = new String[attr.size()];
		keys = (String[])attr.keySet().toArray(keys);
		for (int i = 0; i < keys.length; i++)
		{
			if (keys[i].equals("template"))
			{
				//何もしない
			}
			else if (keys[i].equals("disabled") ||
					keys[i].equals("readonly") ||
					keys[i].equals("checked") ||
					keys[i].equals("ismap"))
			{
				if (attr.getBoolean(keys[i]))
				{
					buf.append(" ").append(keys[i]);
				}
			}
			else
			{
				buf.append(" ").append(keys[i]).append("=\"").append(attr.getString(keys[i])).append("\"");
			}
		}
		buf.append(">");

		try
		{
			JspWriter out = pageContext.getOut();
			out.print(buf.toString());
		}
		catch (IOException e)
		{
			throw new JspException(e.getMessage());
		}

		return super.doStartTag();
	}

	/**
	 * <P>
	 * このタグのタイプを返します。
	 * </P>
	 * @return このタグのタイプ
	 * @see jp.morpho.webapp.servlet.jsp.tagext.TagTemplate#getTagType()
	 */
	public abstract String getTagType();

	/**
	 * <P>
	 * checked 属性が設定されなかった場合、name 属性に設定されている名前をキーにして HTTP パラメータから値を取得し、checked 属性を設定します。
	 * name 属性が設定されていない場合は何もしません。
	 * </P>
	 */
	protected void setCheckedByFormData()
	{
		if (!this.getAttributeMap().containsKey("checked") && this.getAttributeMap().containsKey("name"))
		{
			HttpActionRequest req = new HttpActionRequestImpl((HttpServletRequest)pageContext.getRequest());
			ConversionMap param = ConversionMap.valueOf(req.getParameterMap());
			if (!param.isNull(this.getName()))
			{
				if (param.isArray(this.getName()))
				{
					String values[] = (String[])param.getObject(this.getName());
					String val = this.getValue();
					for (int i = 0; i < values.length; i++)
					{
						if (val.equals(values[i]))
						{
							this.setChecked(true);
							break;
						}
					}
				}
				else
				{
					if (this.getValue().equals(param.getString(this.getName())))
					{
						this.setChecked(true);
					}
				}
			}
		}
	}

	/**
	 * <P>
	 * value 属性が設定されなかった場合、name 属性に設定されている名前をキーにして HTTP パラメータから値を取得し、value 属性に設定します。
	 * name 属性が設定されていない場合は何もしません。
	 * </P>
	 */
	protected void setValueByFormData() throws JspException
	{
		if (!this.getAttributeMap().containsKey("value") && this.getAttributeMap().containsKey("name"))
		{
			HttpActionRequest req = new HttpActionRequestImpl((HttpServletRequest)pageContext.getRequest());
			ConversionMap param = ConversionMap.valueOf(req.getParameterMap());
			HtmlEncoder encoder = this.getTagManager().getHtmlEncoder();
			if (param.containsKey(this.getName()))
			{
				this.getAttributeMap().putString("value", encoder.encode(param.getString(this.getName())));
			}
		}
	}

}
