package org.itscool.weber.tag;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyTagSupport;

import org.itscool.commons.bean.BeanUtil;
import org.itscool.commons.util.JspTagUtil;

/**
 * JSPにセットされた配列にたいして繰り返し処理を実装するタグです<BR>
 * <BR>
 * 要素を含むコレクションは次の形式をサポートします。<BR>
 * <BR>
 * ・java.lang.Object型の配列オブジェクト<BR>
 * ・java.util.Collectionを実装したオブジェクト<BR>
 * ・java.util.Enumerationを実装したオブジェクト（未実装）<BR>
 * ・java.util.Iteratorを実装したオブジェクト<BR>
 * ・java.util.Mapを実装したオブジェクト<BR>
 * <BR>
 * コレクションを取得するには、name属性とscope属性から取得します。<BR>
 *<BR>
 *【属性】<BR>
 * id    : コレクションに含まれる要素を定義するための名前<BR>
 * name  : スコープからコレクションを取得するための名前<BR>
 * property : この属性が指定されている場合は、nameで指定されたオブジェクトから
 * 　　　　　　propety名で指定されたオブジェクトの取得を試みます。
 * scope : コレクションが含まれるスコープ<BR>
 *         scopeが指定されていない場合は次の順序でコレクションを検索します。<BR>
 *         request, session, context<BR>
 *<BR>
 *【使用例】<BR>
 * 以下はIterateTagの使用例です。<P>
 *<BR>
 * &lt;logic:iterate id="ask" name="messages" scope="session"&gt;<BR>
 *        従番：<bean:write name="ask" property="id"/&gt;<BR>
 *        名前：<bean:write name="ask" property="name"/&gt;<BR>
 * &lt;/logic:iterate><BR>
 *<BR>
 * 以下はJSPでハードコーディングした場合の例です<P>
 *<BR>
 * &lt;%<BR>
 *        Collection collection = (LinkedList)pageContext.getSession().
 *          getAttribute("messages");<BR>
 *        Iterator iterator =((Collection) collection).iterator();<BR>
 *        while(iterator.hasNext()){<BR>
 *            Object obj = iterator.next();<BR>
 *            pageContext.getRequest().setAttribute("ask", obj);<BR>
 *    %&gt;<BR>
 *    <BR>
 *    従番：&lt;bean:write name="ask" property="id" scope="request"/&gt;<BR>
 *    名前：&lt;bean:write name="ask" property="name" scope="request" /&gt;<BR>
 *    <BR>
 *  &lt;%<BR>
 *        }<BR>
 *    %&gt;<BR>
 *
 * @author KANO
 * @since jdk1.4.1
 * @version 1.00A 2004/11/1
*/
public class IterateTag extends BodyTagSupport{
    /**
     * nameとscopeにより取得されたIteratorインスタンス
    */
    private Iterator iterator = null;
    /** id属性 */
    private String id;
    /** name属性 */
    private String name;
    /** scope属性 */
    private String scope;
    private String property;
    
    /**
     * id属性を取得します
     * @return id属性を返します
    */
    public String getId(){
        return id;
    }
    
    /**
     * id属性をセットします
     * @param id 属性
    */
    public void setId(String id ){
        this.id = id;
    }
    
    /**
     * name属性を取得します
     * @return name 属性を返します
    */
    public String getName(){
        return name;
    }
    
    /**
     * name属性をセットします
     * param name属性
    */
    public void setName(String name ){
        this.name = name;
    }
    
    /**
     * scope属性を取得します
     * @return scope属性を返します
    */
    public String getScope(){
        return scope;
    }
    
    /**
     * scope属性をセットします
     * @param scope スコープ
    */
    public void setScope(String scope){
        this.scope = scope;
    }
    
    /**
     * プロパティを取得します
     * @return プロパティ
     */
    public String getProperty() {
        return property;
    }

    /**
     * プロパティをセットします
     * @param string プロパティ
     */
    public void setProperty(String string) {
        property = string;
    }

    
    /**
     * BODY内に記述されたロジック又はインスタンスをリクエストスコープに
     * セットされた配列数分実行します<BR>
     * リクエストにセット可能な配列インスタンスは以下のクラスをサポート
     * しています<BR>
     * 　(1) Collection型<BR>
     * 　(2) Iterator型<BR>
     * 　(3) Class[]型<BR>
     * 　(4) Enumeration型（現状未対応)<BR>
     * @return 配列インスタンスが存在する場合はEVAL_BODY_INCLUDEを返し
     *         Body部の評価を行います<BR>
     *         配列インスタンスが存在しない場合はSKIP_BODYを返します
     */
    public int doStartTag() throws JspException {
        Object collection;
        
        //scopeで指定されたスコープから配列インスタンスを取得します
        try{
            collection = JspTagUtil.findObject(pageContext, name, scope);
            if( getProperty() != null ){
                collection = BeanUtil.getProperty(collection, getProperty());
            }
        }catch(Exception e ){
            e.printStackTrace();
            return SKIP_BODY;
            
        }
        
        //スコープが指定されていない場合はスコープを検索します。
        if( scope == null ){
            setScope(JspTagUtil.findScope(pageContext, name));
        }
        
        //配列要素がnullの場合は要素数0の仮の配列インスタンスを作成します
        if( collection == null ){
            collection = new LinkedList();
        }
        
        //配列を構成する型を特定します。
        if (collection.getClass().isArray()) {
            try {
                iterator = Arrays.asList((Object[]) collection).iterator();
            } catch (ClassCastException e) {
                // Rats -- it is an array of primitives
                int length = Array.getLength(collection);
                ArrayList c = new ArrayList(length);
                for (int i = 0; i < length; i++) {
                    c.add(Array.get(collection, i));
                }
                iterator = c.iterator();
            }
        } else if (collection instanceof Collection) {
            iterator = ((Collection) collection).iterator();
        } else if (collection instanceof Iterator) {
            iterator = (Iterator) collection;
        } else if (collection instanceof Map) {
            iterator = ((Map) collection).entrySet().iterator();
        } else if (collection instanceof Enumeration) {
        //    iterator = IteratorUtils.asIterator((Enumeration) collection);
        } else {
            System.out.println("配列オブジェクトではありません");
            
            JspException e = new JspException("配列オブジェクトではありません");
            throw e;
        }
        
        //配列の最初の要素を評価します。
        if (iterator.hasNext()) {
            Object element = iterator.next();
            if (element == null) {
                return (SKIP_BODY);
            }
            
            if( scope.equals(JspTagUtil.SCOPE_APPLICATION)){
                pageContext.getServletContext().setAttribute(id, element);
            }else if( scope.equals(JspTagUtil.SCOPE_SESSION)){
                pageContext.getSession().setAttribute(id, element);
            }else if( scope.equals(JspTagUtil.SCOPE_REQUEST)){
                pageContext.getRequest().setAttribute(id, element);
            }else{
                pageContext.setAttribute(id, element);
            }
            return (EVAL_BODY_INCLUDE);
           
        } else {
            return (SKIP_BODY);
        }
        
    }
    
    /**
     * 配列の次の要素を評価し、要素が存在する場合は
     * Body部を評価します
     * @return 次の要素が存在する場合はEVAL_BODY_AGAINを返し、次の
     *         要素を評価します<BR>
     *         次の要素が存在しない場合はSKIP_BODYを返し、以降は
     *         BODY部の評価を行いません
    */
    public int doAfterBody() throws JspException {
        
        if (iterator.hasNext()) {   //配列の次の要素が存在するか評価します。
        
            Object element = iterator.next();
            if (element == null) {
                return (SKIP_BODY);
            }
            
            if( scope.equals(JspTagUtil.SCOPE_APPLICATION)){
                pageContext.getServletContext().setAttribute(id, element);
            }else if( scope.equals(JspTagUtil.SCOPE_SESSION)){
                pageContext.getSession().setAttribute(id, element);
            }else if( scope.equals(JspTagUtil.SCOPE_REQUEST)){
                pageContext.getRequest().setAttribute(id, element);
            }else{
                pageContext.setAttribute(id, element);
            }
            
            //return (EVAL_BODY_TAG);
            return (EVAL_BODY_AGAIN);
            
        } else {                    //次の要素が無い場合はBODYの評価を行いません
            return (SKIP_BODY);
        }

    }
    
    /**
     * 終わりの処理
    */
    public int doEndTag(){ 
        return EVAL_PAGE;
    }
}
