/*****************************************************************************
 *                                                                           *
 *  This file is part of the tna framework distribution.                     *
 *  Documentation and updates may be get from  biaoping.yin the author of    *
 *  this framework							     *
 *                                                                           *
 *  Sun Public License Notice:                                               *
 *                                                                           *
 *  The contents of this file are subject to the Sun Public License Version  *
 *  1.0 (the "License"); you may not use this file except in compliance with *
 *  the License. A copy of the License is available at http://www.sun.com    * 
 *                                                                           *
 *  The Original Code is tag. The Initial Developer of the Original          *    
 *  Code is biaoping yin. Portions created by biaoping yin are Copyright     *
 *  (C) 2000.  All Rights Reserved.                                          *
 *                                                                           *
 *  GNU Public License Notice:                                               *
 *                                                                           *
 *  Alternatively, the contents of this file may be used under the terms of  *
 *  the GNU Lesser General Public License (the "LGPL"), in which case the    *
 *  provisions of LGPL are applicable instead of those above. If you wish to *
 *  allow use of your version of this file only under the  terms of the LGPL *
 *  and not to allow others to use your version of this file under the SPL,  *
 *  indicate your decision by deleting the provisions above and replace      *
 *  them with the notice and other provisions required by the LGPL.  If you  *
 *  do not delete the provisions above, a recipient may use your version of  *
 *  this file under either the SPL or the LGPL.                              *
 *                                                                           *
 *  biaoping.yin (yin-bp@163.com)                                            *
 *  Author of Learning Java 						     					 *
 *                                                                           *
 *****************************************************************************/
package com.frameworkset.common.tag.pager.tags;

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

import com.frameworkset.common.tag.exception.FormulaException;
import com.frameworkset.tag.logic.CaseTag;
import com.frameworkset.util.StringUtil;
import com.frameworkset.util.ValueObjectUtil;

/**
 * 所有用于分页/列表/详细信息标签逻辑判断的标签基类，
 * 具体的逻辑标签只需实现其中的抽象方法match()即可
 * 
 * @author biaoping.yin
 * @version 1.0
 */
public abstract class MatchTag extends BaseValueTag {
	/**
	 * 检测值得类型是否与typeof对应的类型或者接口的类型一致，或者是其子类
	 * typeof可以是一个表示类路径的String，或者直接是一个Class对象
	 */
	protected Object typeof;
	/**
	 * result evalbody resolvedResult三个属性结合起来为整个逻辑标签提供if-else功能和case-then功能
	 * evalbody 用来指示逻辑标签不管结果如何直接执行标签体
	 * result 用来存放逻辑比较的结果,yes,no,other标签根据result和resolvedResult的值来进行相应的逻辑处理操作
	 * resolvedResult存放内置的yes,no,other的执行情况，只要其中一个执行了，则resolvedResult为true，other标签依赖resolvedResult来决定是否执行other标签体的内容，如果resolvedResult为false则
	 * 执行other标签体的内容，否则不执行，执行完毕后将resolvedResult设置为true
	 */
	private boolean result = false;
	private boolean evalbody = false;
	private boolean resolvedResult = false;
	/**
	 * evalbody为true时，逻辑标签运算完毕后，hasyes和hasno必须都为true，也就是说必须要有yes和no标签一起成对
	 * 出现在逻辑标签中
	 */
	private boolean hasyes;
	private boolean hasno;
	protected CaseTag caseTag = null;
    /**实际值*/
	protected Object actualValue;
	
	/**期望值*/
	protected Object value;
	
	/**表达式，通过计算表达式的结果获取期望值*/
	protected String expressionValue;
	
	/**
	 * 控制是否将真值添加到该字段的枚举值中（扩展字段，生成报表时有用）
	 */
	protected boolean addTruevalue = false; 
	/**
	 * equal和notequal时是否忽略字符串大小写
	 */
	private boolean ignoreCase = false;
	protected String pattern;
	
	/**
	 * in，notin标签匹配的范围枚举值
	 * 可以是变量表达式来指定，例如：{xxxscope}
	 */
	
	protected String scope;
	
	protected String[] scopes;
	protected int offset = -1;
	/**
	 * 用于设置获取集合，字符串长度的变量名称
	 * <pg:notequal length="cell|request|parameter|session|pagecontext:rejectList" value="1">
	 * length属性值带有前缀cell|request|session|pagecontext:
	 * cell 从对象属性中获取属性值得长度
	 * request 从request属性中获取属性值得长度
	 * parameter 从request参数中获取属性值得长度
	 * session 从session属性中获取属性值得长度
	 * pagecontext 从pagecontext属性中获取属性值得长度
	 * 默认从cell中获取对象属性
	 * length属性适用于equal,notequal,upper,lower,upperequal,lowerequal,
	 * in,notin标签
	 */
	protected String length;

	public String getLength() {
		return length;
	}

	public void setLength(String length) {
		this.length = length;
	}
	public boolean isResult() {
		return result;
	}

	public void setResult(boolean result) {
		this.result = result;
	}
	protected void evalLengthInfo()
	{
		
		String[] infos = length.split(":");
		if(infos.length == 1)
		{
			this.setColName(length);
			
		}
		else
		{
			String prefix = infos[0];
			if(prefix.equals("cell"))
			{
				this.setColName(infos[1]);
			}
			else if(prefix.equals("request"))
			{
				this.setRequestKey(infos[1]);
			}
			else if(prefix.equals("parameter"))
			{
				this.setParameter(infos[1]);
			}
			else if(prefix.equals("session"))
			{
				this.setSessionKey(infos[1]);
			}
			else if(prefix.equals("pagecontext"))
			{
				this.setPageContextKey(infos[1]);
			}
		}
		Object _actualValue = evaluateActualValue();
		actualValue = length(_actualValue);
		
	}
	
	
	/**
	 * @return String
	 */
	public Object getValue()
	{
		return value;
	}

	/**
	 * @param string
	 */
	public void setValue(Object string)
	{
		value = string;
	}
	
	public int doStartTag() throws JspException {
	    init();
//	    dataSet = searchDataSet(this,PagerDataSet.class);
//	    t_formula = dataSet.getFormula(getExpression());
	    Tag tag = findAncestorWithClass(this, Tag.class);
	    if(tag != null  && (tag instanceof CaseTag))
	    {
	    	this.caseTag = (CaseTag)tag;
	    	if(caseTag.isResolvedResult())//如果case分支已经被执行过了，则不管条件满不满足都跳出case分支
	    	{
	    		return SKIP_BODY;
	    	}
	    }
	    if(caseTag == null)//是否case-when场景
	    {
		    if(StringUtil.isEmpty(length))
		    	actualValue = evaluateActualValue();
		    else
		    	evalLengthInfo();
	    }
	    else
	    {
	    	actualValue = caseTag.getActualValue();
	    }
	    
//		actualValue = getOutStr();
//		setMeta();
		//如果期望值为表达式，则求解表达式的值来得到期望值
		if(this.getExpressionValue() != null)
            try {
            	Object value_ = getFormulaValue(getExpressionValue());
            	if(value_ != null)
            	{
//            		value = String.valueOf(value_);
            		value = value_;
            	}
            	else
            	{
            		value = null;
            	}
            } catch (FormulaException e) {
                e.printStackTrace();
                return SKIP_BODY;                
            }
		if(!this.evalbody)
		{
			if(result = match())		
				return EVAL_BODY_INCLUDE;
			else
				return SKIP_BODY;
		}
		else
		{
			result = match();
			return EVAL_BODY_INCLUDE;
		}
	}
	
	public int doEndTag() throws JspException 
	{
		int ret = super.doEndTag();
		
		if(!this.evalbody)
		{
			if( this.result)
			{
				if(this.caseTag != null)
				{
					caseTag.setResolvedResult(true);
				}
			}
		}
		else 
		{
			if(!this.hasyes || !this.hasno )
				throw new JspException("标签"+this.getClass().getSimpleName()+"属性evalbody=true时,两个内嵌标签yes和no必须成对出现.");
			if(resolvedResult)
			{
				if(this.caseTag != null)
				{
					caseTag.setResolvedResult(true);
				}
				
			}
		}
		
//		this.requestKey = null ;
//		this.sessionKey= null ;
//		this.pageContextKey= null ;
//		this.parameter= null ;
		
		return ret;
		
	}
	
	protected abstract boolean match();
	
	

    /**
     * @return Returns the addTruevalue.
     */
    public boolean isAddTruevalue() {
        return addTruevalue;
    }
    /**
     * @param addTruevalue The addTruevalue to set.
     */
    public void setAddTruevalue(boolean addTruevalue) {
        this.addTruevalue = addTruevalue;
    }
    /**
     * @return Returns the expressionValue.
     */
    public String getExpressionValue() {
        return expressionValue;
    }
    /**
     * @param expressionValue The expressionValue to set.
     */
    public void setExpressionValue(String expressionValue) {
        this.expressionValue = expressionValue;
    }

	public String getPattern() {
		return pattern;
	}

	public void setPattern(String pattern) {
		this.pattern = pattern;
	}

	public String getScope() {
		return scope;
	}

	public void setScope(String scope) {
		this.scope = scope;
	}

	public String[] getScopes() {
		if(scopes == null)
			calculatescope();
		return scopes;
	}

	private void calculatescope() {
		Object temp = super.getFormulaValueWithAttribute(scope);
		if(temp != null)
		{
			this.scopes = StringUtil.split(temp.toString(),"\\,");
		}
		else
		{
			this.scopes = StringUtil.split(scope,"\\,");
		}
			
		
		
	}

	
	public Object getActualValue()
	{
	
		return actualValue;
	}

	
	public void setActualValue(Object actualValue)
	{
	
		this.actualValue = actualValue;
	}

	
	protected boolean istypeof()
	{
		if(typeof instanceof String)
		{
			try {
				Class clazz = Class.forName((String)typeof);
				if(clazz.isAssignableFrom(this.actualValue.getClass()))
				{
					return true;
				}
				else
					return false;
			} catch (Exception e) {
				throw new java.lang.IllegalArgumentException(typeof +"不存在或者不正确",e);
			}
		}
		else //if(typeof instanceof Class)
		{
			try {
				Class clazz =(Class)typeof;
				if(clazz.isAssignableFrom(this.actualValue.getClass()))
				{
					return true;
				}
				else
					return false;
			} catch (Exception e) {
				throw new java.lang.IllegalArgumentException(typeof +"不存在或者不正确,或者必须是Class类型的对象",e);
			}
		}
	}
	public Object getTypeof() {
		return typeof;
	}
	public void setTypeof(Object typeof) {
		this.typeof = typeof;
	}
	public void doFinally() {
		this.typeof = null;
		this.actualValue = null;
		this.addTruevalue = false; 
		this.value = null;
		this.expressionValue = null;
		this.pattern = null;
		this.scope = null;
		this.scopes = null;
		ignoreCase = false;
		offset  = -1;
		this.resolvedResult = false;
		this.hasno = false;
		this.hasyes =false;
		this.evalbody = false;
		this.result = false;
		this.caseTag = null;
		super.doFinally();
	}

	public boolean isIgnoreCase() {
		return ignoreCase;
	}

	public void setIgnoreCase(boolean ignoreCase) {
		this.ignoreCase = ignoreCase;
	}
	public int getOffset() {
		return offset;
	}
	public void setOffset(int offset) {
		this.offset = offset;
	}
	protected boolean startWith()
	{
		Object temp =  getValue();
		if(actualValue == null && temp != null)
			return false;
		if(actualValue != null && temp == null)
			return false;
		if(actualValue == null && temp == null)
			return true;
		if (actualValue instanceof String && getValue() instanceof String)
		{
			if(this.offset  == -1)
				return ((String)actualValue).startsWith((String)temp);
			else
				return ((String)actualValue).startsWith((String)temp,this.offset);
			
		}
		return false;
	}
	
	
	
	protected boolean endWith()
	{
		Object temp =  getValue();
		if(actualValue == null && temp != null)
			return false;
		if(actualValue != null && temp == null)
			return false;
		if(actualValue == null && temp == null)
			return true;
		if (actualValue instanceof String && getValue() instanceof String)
		{
			return ((String)actualValue).endsWith((String)temp);
			
		}
		return false;
	}
	
	protected boolean equalCompare()
	{
		Object temp =  getValue();
		if(actualValue == null && temp != null)
			return false;
		if(actualValue != null && temp == null)
			return false;
		if(actualValue == null && temp == null)
			return true;
		if (actualValue instanceof String && temp instanceof String)
		{
			if(!this.isIgnoreCase())
			{
				return ((String)actualValue).equals((String)temp);
			}
			else
			{
				return ((String)actualValue).equalsIgnoreCase((String)temp);
			}
		}
		else
		{
			if(ValueObjectUtil.typecompare(actualValue,temp) == 0)
				return true;
			else
				return false;
		}
	}

	public boolean isEvalbody() {
		return evalbody;
	}

	public void setEvalbody(boolean evalbody) {
		this.evalbody = evalbody;
	}

	public boolean isResolvedResult() {
		return resolvedResult;
	}

	public void setResolvedResult(boolean resolvedResult) {
		this.resolvedResult = resolvedResult;
	}

	public boolean isHasyes() {
		return hasyes;
	}

	public void setHasyes(boolean hasyes) {
		this.hasyes = hasyes;
	}

	public boolean isHasno() {
		return hasno;
	}

	public void setHasno(boolean hasno) {
		this.hasno = hasno;
	}
	
	
}
