package org.itscool.commons.util;

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

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

import org.itscool.commons.bean.BeanUtil;
import org.itscool.commons.request.GeneralForm;

/**
 * タグライブラリの共通関数を定義するクラスです。
 * @author KANO
 * @since jdk1.4.1
 * @version 1.00A 2004/11/1
 * @version 1.10A 2006/02/12 GeneralForm対応（Thanks 澤村さん) <br>
 * @version 1.10B 2006/03/07 指定されたタグが配列の場合の対応を追加（Thanks 澤村さん) <br>
 */
public class JspTagUtil extends TagUtil{
    
    /**
     * value属性を取得します。<BR>
     * value属性が次の定型式で表現される場合、指定された変数をスコープから
     * 検索します<BR>
     * <p>
     * value属性の定型式：${オブジェクト名.プロパティ名}<br>
     * <p>
     * 定型式に該当しない場合は、value値をそのまま返します<BR>
     *
     * @param pageContext PageContextインスタンスを指定します。
     * @param value value属性
     * @param scope Beanのスコープを指定します。
     * @return value属性の結果を返します
     */
    public static Object findValue(PageContext pageContext, String value, String scope)
    throws JspException{
        if( value == null ){
            return "";
        }else if(value.length() < 3 ){
            return value;
        }
        String topKey = value.substring(0,1);
        String stKakko = value.substring(1,2);
        String edKakko = value.substring(value.length()-1,value.length());
        
        if(!topKey.equals("$")){
            return value;
        }
        StringTokenizer propertySt = new StringTokenizer(value, ".");
        String name = propertySt.nextToken().trim();
        name = name.substring(2, name.length());
        String property = null;
        if(propertySt.hasMoreTokens()){
            property = propertySt.nextToken().trim();
            property = property.substring(0, property.length()-1);
        }else{
            name = name.substring(0, name.length()-1);
        }
//        System.out.print("[" + name +"]");
//        System.out.println("["+property+"]");
        
        Object result = findProperty(pageContext, name, property, scope);
        if( result == null){
            return "";
        }else{
            return result;
        }
    }
    
    /**
     * PageContextインスタンスから指定されたBeanを取得します。
     * @param pageContext PageContextインスタンスを指定します。
     * @param name pageContextにセットされたBeanの名前を指定します。
     * @param scope Beanのスコープを指定します。
     * @return 見つかったオブジェクトを返します
     */
    public static Object findObject(PageContext pageContext,
            String name, String scope){
        Object obj = lookup(pageContext, name, scope);
        
        return obj;
        
    }
    
    /**
     * PageContextインスタンスから指定されたBeanのプロパティを取得します。
     * @param pageContext PageContextインスタンスを指定します。
     * @param name pageContextにセットされたBeanの名前を指定します。
     * @param property pageContextにセットされたBeanのプロパティ名を指定します。
     * @param scope Beanのスコープを指定します。
     * @return 見つかったプロパティを返します
     * @throws JspException
     */
    public static Object findProperty(PageContext pageContext,
            String name, String property, String scope) throws JspException{
        Object pObj = null;
        Object obj = null;
        
        if(name != null && name.length() != 0){
            obj = lookup(pageContext, 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(pageContext, property, scope);
            
        }else if( obj != null && property == null ){
            pObj = obj;
        }
        
        if(pObj == null){
            pObj = new String("");
        }
        
        return pObj;
    }
    
    /**
     * 次の順序でnameに指定されたオブジェクトのスコープを検索します<BR>
     * request, session, applicationの順
     * @param pageContext PageContextインスタンスを指定します
     * @param name PageContextインスタンスにセットされているBean名を指定します
     * @return スコープを返します
     */
    public static String findScope(PageContext pageContext, String name){
        Object obj = null;
        
        obj = pageContext.getAttribute(name);
        if( obj != null ){
            return SCOPE_PAGE;
        }
        
        obj = pageContext.getRequest().getAttribute(name);
        if( obj != null ){
            return SCOPE_REQUEST;
        }
        
        obj = pageContext.getSession().getAttribute(name);
        if( obj != null ){
            return SCOPE_SESSION;
        }
        
        obj = pageContext.getServletContext().getAttribute(name);
        if( obj != null){
            return SCOPE_APPLICATION;
        }
        
        return null;
        
    }
    
    /**
     * PageContextインスタンスから指定されたBeanを取得します。<BR>
     * scopeが指定されていない場合は、次の順序でオブジェクトを検索します。<BR>
     * request, session, contextの順
     * @param pageContext PageContextインスタンスを指定します。
     * @param name pageContextにセットされたBeanの名前を指定します。
     * @param scope Beanのスコープを指定します。
     * @return オブジェクトを返します
     */
    protected static Object lookup(PageContext pageContext, String name, String scope) {
        Object obj = null;
        
        if(scope == null){
            //scopeが指定されていない場合は次の順序でオブジェクトを検索します。
            // request, session, context
            obj = lookup(pageContext, name);
            
            return obj;
        }
        
        if( scope.equals(SCOPE_APPLICATION) ){
            obj = pageContext.getServletContext().getAttribute(name);
            
        }else if( scope.equals(SCOPE_SESSION) ){
            obj = pageContext.getSession().getAttribute(name);
            
        }else if( scope.equals(SCOPE_REQUEST) ){
            obj = pageContext.getRequest().getAttribute(name);
            
        }else if( scope.equals(SCOPE_PAGE)){
            obj = pageContext.getAttribute(name);
        }
        
        return obj;
    }
    
    /**
     * 次の順序でPageContextインスタンスから指定されたBeanを取得します。<BR>
     * request, session, contextの順
     * @param pageContext PageContextインスタンスを指定します。
     * @param name pageContextにセットされたBeanの名前を指定します。
     * @return オブジェクトを返します
     */
    protected static Object lookup(PageContext pageContext, String name){
        Object obj = null;
        
        obj = pageContext.getAttribute(name);
        if( obj != null ){
            return obj;
        }
        
        obj = pageContext.getRequest().getAttribute(name);
        if( obj != null ){
            return obj;
        }
        
        obj = pageContext.getSession().getAttribute(name);
        if( obj != null ){
            return obj;
        }
        
        obj = pageContext.getServletContext().getAttribute(name);
        if( obj != null ){
            return obj;
        }
        
        obj = pageContext.getServletContext().getAttribute(name);
        if( obj != null ){
            return obj;
        }
        return obj;
        
    }
    
    /**
     * [ value="value" ]形式のタグを作成します
     * @param pageContext PageContextインスタンスを指定します
     * @param name   PageContextインスタンスに保持されているオブジェクトの論理名称
     *             を指定します
     * @param property オブジェクトのプロパティ名を指定します
     * @param scope オブジェクトがPageContextインスタンスに保持されているスコープ
     *              を指定します。
     * @param value value属性に指定する値を指定します<BR>
     *              value属性がnullの場合はオブジェクトにセットされている値を使用
     *              します
     * @return タグを返します
     */
    public static String createAttribute(PageContext pageContext,
            String name, String property, String scope, String key, String value ) throws JspException{
        StringBuffer inputTag = new StringBuffer();
        
        if( property == null || property.length() == 0 ){
            inputTag.append("");
            
        }else{
            Object result = JspTagUtil.findProperty(pageContext, name, property, scope);
            if( value != null && value.toString().length() > 0){
                
                //EL式対応バージョン
                value = JspTagUtil.findValue(pageContext, value, scope).toString();
                
                //Value属性が指定されている場合は
                //Value属性を出力する
                inputTag.append(" " + key + "=\"").append(value).append("\" ");
            }else if( result == null || result.toString().length() == 0 ){
                //Beanがセットされていない場合は、プロパティ単体（name）
                //で値がセットされていないか確認する。
                //入力エラーでこのページに戻って来た場合は以下のプロパティ
                //がセットされる。
                result = (String)pageContext.getRequest().getAttribute(property);
                if( result != null && result.toString().length() > 0 ){
                    inputTag.append(" "+ key + "=\"").append(result).append("\" ");
                    
                    //プロパティがセットされていない場合はデフォルト（空）
                    //を出力する
                }else{
                    //inputTag.append(name).append("\" ");
                    inputTag.append("");
                    
                }
            }else{
                //Beanがセットされている場合はBeanにセットされている
                //プロパティを出力する
                
                inputTag.append(" " + key + "=\"").append(result).append("\" ");
            }
        }
        
        return inputTag.toString();
    }
    
    /**
     * [ ${tagName}="${tagValue}" ]形式のタグを作成します
     * @param tagName 属性を指定します
     * @param tagValue 属性にセットする値を指定します
     * @return タグを返します
     */
    public static String createAttribute(String tagName, String tagValue){
        StringBuffer inputTag = new StringBuffer(" ").append(tagName);
        inputTag.append("=\"");
        inputTag.append(tagValue).append("\" ");
        
        if( tagName == null || tagName.length() == 0 ||
                tagValue == null || tagValue.length() == 0){
            return "";
        }else{
            return inputTag.toString();
        }
    }
    
    /**
     * [ ${tagName}="${tagValue(true/false)}" ]形式のタグを作成します
     * @param tagName 属性を指定します
     * @param tagValue 属性にセットする値を指定します
     * @return タグを返します
     */
    public static String createBoolAttribute(String tagName, String tagValue){
        StringBuffer inputTag = new StringBuffer(" ").append(tagName);
        
        if( tagName == null || tagName.length() == 0 || tagValue == null ){
            return "";
        }else if(tagValue.toLowerCase().equals("true") ){
            inputTag.append(createAttribute(tagName, "true"));
            return inputTag.toString();
        }else{
            inputTag.append(createAttribute(tagName, "false"));
            return inputTag.toString();
        }
    }
    
    
    
    /**
     * プロパティ名からオブジェクトのプロパティを取得し、結果をクエリー文字列で返します。
     * <BR>
     * プロパティリストには以下の書式である必要があります。
     * <P>
     * オブジェクト名:オブジェクト取得時のプロパティ名（省略可能):ＨＴＭＬに出力するときのプロパティ名（省略可能),
     * オブジェクト名2:プロパティ名2,ＨＴＭＬに出力するときのプロパティ名2（省略可能),･･･(以降パラメータ数分繰り返し)
     *
     * <p>
     * ※ プロパティ名を省略した場合はオブジェクト名で取得したオブジェクトのtoString()を返します
     * @param pageContext PageContextインスタンス
     * @param properties プロパティリスト
     * @param scope スコープ
     * @return クエリー文字列を返します
     * @throws JspException
     */
    public static String createQueryString(PageContext pageContext,
            String properties, String scope) throws JspException{
        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(pageContext, name, scope);
            }else{
                property = property.trim();
                value = findProperty(pageContext, 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();
    }
    
    public static String createRealPath(PageContext pageContext, String path){
        if( path == null){
            path = "/";
        }else if( path.indexOf("/") != 0 ){
            path += "/";
        }
        HttpServletRequest req = (HttpServletRequest)pageContext.getRequest();
        String realPath = req.getContextPath();
        return realPath + path;
    }
    
}
