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;

/**
 * タグライブラリの共通関数を定義するクラスです。
 * @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;
        
    }
    
//    /**
//     * [ value="value" ]形式のタグを作成します
//     * @param servletContext ServletContextインスタンスを指定します
//     * @param name   ServletContextインスタンスに保持されているオブジェクトの論理名称
//     *             を指定します
//     * @param property オブジェクトのプロパティ名を指定します
//     * @param scope オブジェクトがServletContextインスタンスに保持されているスコープ
//     *              を指定します。
//     * @param value value属性に指定する値を指定します<BR>
//     *              value属性がnullの場合はオブジェクトにセットされている値を使用
//     *              します
//     * @return タグを返します
//    */
//    public static String createAttribute(ServletContext servletContext, HttpServletRequest req,
//    String name, String property, String scope, String key, String value ) {
//        StringBuffer inputTag = new StringBuffer();
//
//        if( property == null || property.length() == 0 ){
//            inputTag.append("");
//
//        }else{
//            Object result = TagUtil2.findProperty(servletContext, req, name, property, scope);
//            if( value != null && value.toString().length() > 0){
//
//                //EL式対応バージョン
//            	value = TagUtil2.findValue(servletContext, req, value, scope).toString();
//
//            	//Value属性が指定されている場合は
//                //Value属性を出力する
//                inputTag.append(" " + key + "=\"").append(value).append("\" ");
//            }else if( result == null || result.toString().length() == 0 ){
//                //Beanがセットされていない場合は、プロパティ単体（name）
//                //で値がセットされていないか確認する。
//                //入力エラーでこのページに戻って来た場合は以下のプロパティ
//                //がセットされる。
//            	result = (String)req.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();
//		}
//	}
    
//    /**
//     * タグに指定された値とBeanにセットされた値が同じであるかチェックします<br>
//     * @param beanVal オブジェクトにセットされているプロパティ値
//     * @param value タグに指定された値
//     * @return タグに指定された値とBeanにセットされた値が同じ場合はtrueを返します
//     *
//    */
//    public static boolean isCheckedValue(Object beanVal, String value){
//        boolean ret = false;
//        if( beanVal == null || value == null || value.length() == 0){
//            return ret;
//        }
////add 2006/03/07 Mr.sawamura start
//        if ( beanVal instanceof List ) {
//            List al = (List)beanVal;
//            for (int i = 0; i < al.size(); i++) {
//            	Object bv = al.get(i);
//            	boolean b = isCheckedValue(bv, value);
//                if (b) {
//                     ret = true;
//                     break;
//                  }
//              }
//       }
////add 2006/03/07 Mr.sawamura end
//       else if( beanVal instanceof Boolean ){
//            Boolean chkVal = new Boolean(value);
//
//            if( beanVal != null && beanVal.toString().equals(chkVal.toString())){
//                ret = true;
//            }
//        }else if( beanVal instanceof String ){
//            String chkVal = new String(value);
//
//            if( beanVal != null && beanVal.toString().equals(chkVal.toString())){
//                ret = true;
//            }
//        }else if( beanVal instanceof Integer ){
//            Integer chkVal = new Integer(value);
//
//            if( beanVal != null && beanVal.toString().equals(chkVal.toString())){
//                ret = true;
//            }
//        }else if( beanVal instanceof Double ){
//            Double chkVal = new Double(value);
//
//            if( beanVal != null && beanVal.toString().equals(chkVal.toString())){
//                ret = true;
//            }
//        }else if( beanVal instanceof Float ){
//            Float chkVal = new Float(value);
//
//            if( beanVal != null && beanVal.toString().equals(chkVal.toString())){
//                ret = true;
//            }
//        }else if( beanVal instanceof Long ){
//            Long chkVal = new Long(value);
//
//            if( beanVal != null && beanVal.toString().equals(chkVal.toString())){
//                ret = true;
//            }
//        }else{
//            String chkVal = new String(value);
//
//            if( beanVal != null && beanVal.toString().equals(chkVal.toString())){
//                ret = true;
//            }
//        }
//        return ret;
//    }
    
//    /**
//     * HTML記述形式の文字列から指定されたタグ内の文字列を取得します
//     *
//     * @param html HTML記述の文字列
//     * @param tag タグ名を指定します（body、head、titleなど）
//     * @return TagInfoインスタンスを返します
//     *          HTML記述が不正な場合はNULLを返します
//     */
//    public static TagInfo createTagInfo(String html, String tag){
//        int startIndex = html.indexOf("<"+ tag);
//        int endIndex = html.indexOf("</" + tag + ">");
//
//        if( startIndex == -1 || endIndex == -1){
//            return null;
//        }
//
//        String body = html.substring(startIndex, endIndex + ("</" + tag + ">").length());
//
//        int startBodyIndex = body.indexOf(">") + 1;
//        int endBodyIndex = body.indexOf("</" + tag + ">");
//
//        if( endBodyIndex == -1 ){
//            return null;
//        }
//
//        String bodyIn = body.substring(startBodyIndex, endBodyIndex);
//
//        TagInfo tagInfo = new TagInfo();
//        tagInfo.setDataInTag(bodyIn);
//        tagInfo.setStartTag(body.substring(0, startBodyIndex));
//        tagInfo.setEndTag(body.substring(endBodyIndex, body.length()));
//
//        return tagInfo;
//    }
    
    /**
     * プロパティ名からオブジェクトのプロパティを取得し、結果をクエリー文字列で返します。
     * <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;
//    	}
//    	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(servletContext, req, name, property, scope);
//        if( result == null){
//        	return "";
//        }else{
//        	return result;
//        }
//    }
    
    /**
     * 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;
        }
        
        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( size > 2 ){
                for( int i=2; i<size; i++ ){
                    property = elems.getElement(i);
                    obj = BeanUtil.getProperty(obj, property);
//					obj = getProperty(obj, property);
                }
            }
            return obj;
        }
    }
    
    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);
    }
}
