package org.itscool.commons.util;

import java.util.Hashtable;
import java.util.LinkedList;
import java.util.Map;
import java.util.StringTokenizer;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;

import org.itscool.commons.bean.BeanUtil;
import org.itscool.commons.request.GeneralForm;
import org.itscool.stylist.el.LayoutRegex;

/**
 * タグライブラリの共通関数を定義するクラスです。
 * @author KANO
 * @since jdk1.4.1
 */
public class ServletTagUtil extends TagUtil{
    
    /**
     * ServletContextインスタンスから指定されたBeanを取得します。
     * @param servletContext ServletContextインスタンスを指定します。
     * @param req HttpServletRequestインスタンスを指定します
     * @param name servletContextにセットされたBeanの名前を指定します。
     * @param scope Beanのスコープを指定します。
     * @return 見つかったオブジェクトを返します
     */
    public static Object findObject(ServletContext servletContext,
        HttpServletRequest req, String name, String scope){
        Object obj = lookup(servletContext, req, name, scope);
        
        return obj;
        
    }
    
    /**
     * ServletContextインスタンスから指定されたBeanのプロパティを取得します。
     * @param servletContext ServletContextインスタンスを指定します。
     * @param req HttpServletRequestインスタンスを指定します
     * @param name servletContextにセットされたBeanの名前を指定します。
     * @param property servletContextにセットされたBeanのプロパティ名を指定します。
     * @param scope Beanのスコープを指定します。
     * @return 見つかったプロパティを返します
     */
    public static Object findProperty(ServletContext servletContext, HttpServletRequest req,
        String name, String property, String scope) {
        Object pObj = null;
        Object obj = null;
        
        if(name != null && name.length() != 0){
            obj = lookup(servletContext, req, name, scope);
        }
        
        if( obj != null && property != null){
//modify 2006.02.12 GeneralForm対応 start
            if( obj instanceof GeneralForm ){
                pObj = ((GeneralForm)obj).getParameter(property);
//add 2006.03.14 Hash対応 start
            }else if(obj instanceof Map ){
                pObj = ((Map)obj).get(property);
            }else if(obj instanceof Hashtable ){
                pObj = ((Hashtable)obj).get(property);
//add 2006.03.14 Hash対応 end
            }else{
                pObj = BeanUtil.getProperty(obj, property);
            }
//modify 2006.02.12 GeneralForm対応 end
        }else if( obj == null && property != null ){
            pObj = lookup(servletContext, req, property, scope);
            
        }else if( obj != null && property == null ){
            pObj = obj;
        }
        
        if(pObj == null){
            pObj = new String("");
        }
        
        return pObj;
    }
    
    /**
     * 次の順序でnameに指定されたオブジェクトのスコープを検索します<BR>
     * request, session, applicationの順
     * @param servletContext ServletContextインスタンスを指定します
     * @param req HttpServletRequestインスタンスを指定します
     * @param name ServletContextインスタンスにセットされているBean名を指定します
     * @return スコープを返します
     */
    public static String findScope(ServletContext servletContext,
        HttpServletRequest req, String name){
        Object obj = null;
        
        
        obj = req.getAttribute(name);
        if( obj != null ){
            return SCOPE_REQUEST;
        }
        
        obj = req.getSession().getAttribute(name);
        if( obj != null ){
            return SCOPE_SESSION;
        }
        
        obj = servletContext.getAttribute(name);
        if( obj != null){
            return SCOPE_APPLICATION;
        }
        
        return null;
        
    }
    
    /**
     * ServletContextインスタンスから指定されたBeanを取得します。<BR>
     * scopeが指定されていない場合は、次の順序でオブジェクトを検索します。<BR>
     * request, session, contextの順
     * @param servletContext ServletContextインスタンスを指定します。
     * @param req HttpServletRequestインスタンスを指定します
     * @param name servletContextにセットされたBeanの名前を指定します。
     * @param scope Beanのスコープを指定します。
     * @return オブジェクトを返します
     */
    protected static Object lookup(ServletContext servletContext,
        HttpServletRequest req, String name, String scope) {
        Object obj = null;
        
        if(scope == null){
            //scopeが指定されていない場合は次の順序でオブジェクトを検索します。
            // request, session, context
            obj = lookup(servletContext,req, name);
            
            return obj;
        }
        
        if( scope.equals(SCOPE_APPLICATION) ){
            obj = servletContext.getAttribute(name);
            
        }else if( scope.equals(SCOPE_SESSION) ){
            obj = req.getSession().getAttribute(name);
            
        }else if( scope.equals(SCOPE_REQUEST) ){
            obj = req.getAttribute(name);
            
        }
        
        return obj;
    }
    
    /**
     * 次の順序でServletContextインスタンスから指定されたBeanを取得します。<BR>
     * request, session, contextの順
     * @param servletContext ServletContextインスタンスを指定します。
     * @param req HttpServletRequestインスタンスを指定します
     * @param name servletContextにセットされたBeanの名前を指定します。
     * @return オブジェクトを返します
     */
    protected static Object lookup(ServletContext servletContext,
        HttpServletRequest req, String name){
        Object obj = null;
        
        obj = req.getAttribute(name);
        if( obj != null ){
            return obj;
        }
        
        obj = req.getSession().getAttribute(name);
        if( obj != null ){
            return obj;
        }
        
        obj = servletContext.getAttribute(name);
        if( obj != null ){
            return obj;
        }
        
        return obj;
        
    }
    
    /**
     * プロパティ名からオブジェクトのプロパティを取得し、結果をクエリー文字列で返します。
     * <BR>
     * プロパティリストには以下の書式である必要があります。
     * <P>
     * オブジェクト名:オブジェクト取得時のプロパティ名（省略可能):ＨＴＭＬに出力するときのプロパティ名（省略可能),
     * オブジェクト名2:プロパティ名2,ＨＴＭＬに出力するときのプロパティ名2（省略可能),･･･(以降パラメータ数分繰り返し)
     *
     * <p>
     * ※ プロパティ名を省略した場合はオブジェクト名で取得したオブジェクトのtoString()を返します
     * @param servletContext ServletContextインスタンス
     * @param req HttpServletRequestインスタンスを指定します
     * @param properties プロパティリスト
     * @param scope スコープ
     * @return クエリー文字列を返します
     */
    public static String createQueryString(ServletContext servletContext,
        HttpServletRequest req, String properties, String scope) {
        if(properties==null || properties.length() == 0){
            return "";
        }
        StringTokenizer dataSt = new StringTokenizer(properties, ",");
        StringBuffer sb = new StringBuffer("");
        if(dataSt.hasMoreTokens()){
            sb.append("?");
        }
        while (dataSt.hasMoreTokens()) {
            String data = dataSt.nextToken().trim();
            StringTokenizer propertySt = new StringTokenizer(data, ":");
            //オブジェクト名の取得
            String name = propertySt.nextToken();
            if( name==null){
                throw new RuntimeException("name is null query="+properties);
            }else{
                name = name.trim();
            }
            //プロパティ名の取得
            String property = null;
            if( propertySt.hasMoreTokens() ){
                property = propertySt.nextToken();
            }
            //HTMLに出力するプロパティ名の取得
            String htmlName = null;
            if( propertySt.hasMoreTokens() ){
                htmlName = propertySt.nextToken().trim();
            }
            Object value = null;
            if( property == null ){
                value = findObject(servletContext, req, name, scope);
            }else{
                property = property.trim();
                value = findProperty(servletContext, req, name, property, scope);
            }
            if( htmlName != null ){
                sb.append(htmlName).append("=");
            }else if( property == null ){
                sb.append(name).append("=");
            }else{
                sb.append(property).append("=");
            }
            if( value != null){
                sb.append(value.toString());
            }
            if(dataSt.hasMoreTokens()){
                sb.append("&");
            }
            //println(st.nextToken());
        }
        return sb.toString();
    }
    
    /**
     * value属性を取得します。<BR>
     * value属性が次の定型式で表現される場合、指定された変数をスコープから
     * 検索します<BR>
     * <p>
     * value属性の定型式：${オブジェクト名.プロパティ名}<br>
     * <p>
     * 定型式に該当しない場合は、value値をそのまま返します<BR>
     *
     * @param servletContext ServletContextインスタンスを指定します。
     * @param req HttpServletRequestインスタンスを指定します
     * @param value value属性
     * @param scope Beanのスコープを指定します。
     * @return value属性の結果を返します
     */
    public static Object findValue(ServletContext servletContext,
        HttpServletRequest req, String value, String scope){
        if( value == null ){
            return "";
        }else if(value.length() < 3 ){
            return value;
        }
        
//modify 2007/05/07 kano start
        if( value.indexOf("${") >= 0 ){
            LayoutRegex regex = new LayoutRegex(servletContext, req);
            Object obj = regex.replaceEl(value);
            return obj;
        }else{
            return value;
        }
        
//        ElementList elems = parseEl(value);
//        int size = elems.size();
//        if( size == 0 ){
//            return value;
//        }else if( size == 1 ){
//            String name = elems.getElement(0);
//            Object obj = findObject(servletContext, req, name, scope);
//            return obj;
//        }else{
//            String name = elems.getElement(0);
//            String property = elems.getElement(1);
//            Object obj = findProperty(servletContext, req, name, property, scope);
//            if( obj == null || obj.toString().length() == 0 ){
//                return obj;
//            }
//            if( size > 2 ){
//                for( int i=2; i<size; i++ ){
//                    property = elems.getElement(i);
//                    obj = BeanUtil.getProperty(obj, property);
//                    if( obj == null || obj.toString().length() == 0 ){
//                        return obj;
//                    }
//                }
//            }
//            return obj;
//        }
//modify 2007/05/07 kano end
    }
    
    public static ElementList parseEl(String value){
        ElementList elems = new ElementList();
        String property = null;
        if( value == null || value.equals("")){
            return elems;
        }else if(value.length() < 3 ){
            return elems;
        }
        int startIndex = value.indexOf("${");
        int lastIndex = value.lastIndexOf("}");
        
        if(startIndex < 0 || lastIndex < 0){
            return elems;
        }
        property=value.substring(startIndex+2, lastIndex);
        if(property != null && property.length() > 0){
            property = property.trim();
        }
//    	System.out.print(property + " ==> ");
        StringTokenizer propertySt = new StringTokenizer(property, ".");
        while(propertySt.hasMoreTokens()){
            property = propertySt.nextToken().trim();
            property = property.substring(0, property.length());
            elems.add(property);
        }
//        System.out.println(elems.toString());
        return elems;
    }
}

class ElementList extends LinkedList{
    public static final String LINE=System.getProperty("line.separator");
    public String toString(){
        StringBuffer sb = new StringBuffer();
        int listSize = this.size();
        for(int i=0; i<listSize; i++ ){
            String str = (String)this.get(i);
            sb.append(str);
            if( (i+1) < listSize ){
                sb.append(", ");
            }
        }
        
        return sb.toString();
    }
    
    public String getElement(int index){
        return (String)this.get(index);
    }
}
