/* 
 * Copyright (C) 2002-2012 XimpleWare, [email protected]
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
package com.ximpleware.extended;
//import com.ximpleware.VTDNav;
import com.ximpleware.extended.xpath.Alist;
import com.ximpleware.extended.xpath.Expr;
import com.ximpleware.extended.xpath.FuncName;
import com.ximpleware.extended.xpath.UnsupportedException;
/**
 * FuncExpr implements the function expression defined
 * in XPath spec
 * 
 */
public class FuncExpr extends Expr{

	public Alist argumentList;
	public int opCode;
	boolean isNumerical;
	boolean isBoolean;
	boolean isString;
	int contextSize;
	//double d;
	int position;
	int a;
	int argCount(){
		Alist temp = argumentList;
		int count = 0;
		while(temp!=null){
			count++;
			temp = temp.next;
		}
		return count;
	}
	public FuncExpr(int oc , Alist list){
		a = 0;
	  opCode = oc;
	  argumentList = list;
	  isBoolean = false;
	  isString  = false;
	  position = 0;
	  //isNodeSet = false;
	  isNumerical = false;
	  switch(opCode){
			case FuncName.LAST: 			isNumerical = true;break;
			case FuncName.POSITION: 		isNumerical = true;break;
			case FuncName.COUNT: 			isNumerical = true;break;
			case FuncName.LOCAL_NAME: 		isString = true; break;
			case FuncName.NAMESPACE_URI: 	isString = true; break;
			case FuncName.NAME: 			isString = true; break;
			case FuncName.STRING: 			isString = true; break;
			case FuncName.CONCAT: 			isString = true; break;
			case FuncName.STARTS_WITH:		isBoolean= true;break;
			case FuncName.CONTAINS: 		isBoolean= true;break;
			case FuncName.SUBSTRING_BEFORE: isString = true; break;
			case FuncName.SUBSTRING_AFTER: 	isString = true; break;
			case FuncName.SUBSTRING: 		isString = true; break;
			case FuncName.STRING_LENGTH: 	isNumerical = true;break;
			case FuncName.NORMALIZE_SPACE: 	isString = true; break;
			case FuncName.TRANSLATE:	 	isString = true;break;
			case FuncName.BOOLEAN: 			isBoolean =true;break;
			case FuncName.NOT: 			    isBoolean =true;break;
			case FuncName.TRUE: 			isBoolean = true;break;
			case FuncName.FALSE: 			isBoolean = true;break;
			case FuncName.LANG: 			isBoolean = true;break;
			case FuncName.NUMBER:			isNumerical = true;break;
			case FuncName.SUM: 			    isNumerical = true;break;
			case FuncName.FLOOR: 			isNumerical = true;break;
			case FuncName.CEILING: 			isNumerical = true;break;
			case FuncName.ROUND:			isNumerical = true;break;
			case FuncName.ABS:				isNumerical = true;break;
			case FuncName.ROUND_HALF_TO_EVEN :
			    							isNumerical = true;break;
			case FuncName.ROUND_HALF_TO_ODD:
			    							isNumerical = true;break;
			case FuncName.CODE_POINTS_TO_STRING:
			    							isString = true; break;
			case FuncName.COMPARE:			isBoolean= true;break;
			case FuncName.UPPER_CASE:		isString = true; break;
			case FuncName.LOWER_CASE:		isString = true; break;
			case FuncName.ENDS_WITH:		isBoolean= true;break;
			case FuncName.QNAME:			isString = true; break;
			case FuncName.LOCAL_NAME_FROM_QNAME:
			    							isString = true; break;
			case FuncName.NAMESPACE_URI_FROM_QNAME:
			    							isString = true; break;
			case FuncName.NAMESPACE_URI_FOR_PREFIX:
			    							isString = true; break;
			case FuncName.RESOLVE_QNAME:	isString = true; break;
			case FuncName.IRI_TO_URI:    	isString = true; break;
			case FuncName.ESCAPE_HTML_URI:	isString = true; break;
			default:						isString = true; break;
	  }	  
	}

	public String toString(){
	  if (argumentList == null)
		  return fname()+" ("+")";
	  return fname()+" ("+argumentList +")";
	}
	
	private String getLocalName(VTDNavHuge vn){
	    if (argCount()== 0){
	        try{
	            int index = vn.getCurrentIndex();
	            int type = vn.getTokenType(index);
	            if (vn.ns && (type == VTDNavHuge.TOKEN_STARTING_TAG 
	                    || type == VTDNavHuge.TOKEN_ATTR_NAME)) {
                    long offset = vn.getTokenOffset(index);
                    int length = vn.getTokenLength(index);
                    if (length < 0x10000)
                        return vn.toRawString(index);
                    else {
                        int preLen = length >> 16;
                        int QLen = length & 0xffff;
                        if (preLen != 0)
                            return vn.toRawString(offset + preLen+1, QLen
                                    - preLen - 1);
                        else {
                            return vn.toRawString(offset, QLen);
                        }
                    }
                } else
                    return "";
	        }catch(NavExceptionHuge e){
	            return ""; // this will almost never occur
	        }
	        
	    } else if (argCount() == 1){
	        int a = -1;
			vn.push2();
			try{
				a = argumentList.e.evalNodeSet(vn);						
				argumentList.e.reset(vn);
				vn.pop2();						
			}catch(Exception e){
				argumentList.e.reset(vn);
				vn.pop2();
			}
			
			if (a == -1 || vn.ns == false)
			    return "";
			int type = vn.getTokenType(a);
			if (type!=VTDNavHuge.TOKEN_STARTING_TAG && type!= VTDNavHuge.TOKEN_ATTR_NAME)
			    return "";
			try {			    
			    long offset = vn.getTokenOffset(a);
			    int length = vn.getTokenLength(a);
			    if (length < 0x10000)
			        return vn.toRawString(a);
			    else {
			        int preLen = length >> 16;
			        int QLen = length & 0xffff;
			        if (preLen != 0)
			            return vn.toRawString(offset + preLen+1, 
			                    QLen - preLen - 1);
			        else {
			            return vn.toRawString(offset, QLen);
			        }
			    }
			} catch (NavExceptionHuge e) {
			    return ""; // this will almost never occur
			}							        
	    } else 
	        throw new IllegalArgumentException
			("local-name()'s argument count is invalid");
	}
	
	private String getNameSpaceURI(VTDNavHuge vn){
	    if (argCount()==0){
	        try{
	            int i = vn.getCurrentIndex();
	            int type = vn.getTokenType(i);
	            
                if (vn.ns && (type == VTDNavHuge.TOKEN_STARTING_TAG 
	                    || type == VTDNavHuge.TOKEN_ATTR_NAME)) {
                    int a = vn.lookupNS();
                    if (a == 0)
                        return "";
                    else
                        return vn.toString(a);
                }
	            return "";
	        }catch (Exception e){
	            return "";
	        }
	    }else if (argCount()==1){
	    	vn.push2();
	        int size = vn.contextStack2.size;
	        int a = -1;
	        try {
	            a = argumentList.e.evalNodeSet(vn);	            
	        } catch (Exception e) {
	        }
	        String s="";
	       // return a;
			try {
                if (a == -1 || vn.ns == false)
                   ;
                else {
                    int type = vn.getTokenType(a);
                    if (type == VTDNavHuge.TOKEN_STARTING_TAG
                            || type == VTDNavHuge.TOKEN_ATTR_NAME)
                       s= vn.toString(vn.lookupNS());
                    
                }                
            } catch (Exception e){} ;
            vn.contextStack2.size = size;
	        argumentList.e.reset(vn);
	        vn.pop2();
            return s;
			
	    }else 
	        throw new IllegalArgumentException
			("namespace-uri()'s argument count is invalid");
	}
	
	private String getName(VTDNavHuge vn){
	    int a;
	    if (argCount()==0){
	        a = vn.getCurrentIndex();
	        int type = vn.getTokenType(a);
            
            if (type == VTDNavHuge.TOKEN_STARTING_TAG 
                    || type == VTDNavHuge.TOKEN_ATTR_NAME){
	            try{
	                return vn.toString(a);
	            }catch(Exception e){
	                return "";
	            }            
	        }
	        else 
	            return "";
	    } else if (argCount() == 1){
	    	a = evalFirstArgumentListNodeSet2(vn);
			try {
                if (a == -1 || vn.ns == false)
                    return "";
                else {
                    int type = vn.getTokenType(a);
                    if (type == VTDNavHuge.TOKEN_STARTING_TAG
                            || type == VTDNavHuge.TOKEN_ATTR_NAME)
                        return vn.toString(a);
                    return "";
                }
            } catch (Exception e) {
            }			
			return "";
	    }else 
	        throw new IllegalArgumentException
			("name()'s argument count is invalid");
	        
	}
	// ISO 639 
	// http://www.loc.gov/standards/iso639-2/php/English_list.php
	// below are defined two-letter words
	// 
	// ab , aa , af, ak, sq, am, ar, an, hy, as, av, ae, ay, az, bm
	// ba , eu , be, bn, bh, bi, nb, bs, br, bg, my, es, ca, km, ch
	// ce , ny , zh, za, cu, cv, kw, co, cr, hr, cs, da, dv, dv, nl
	// dz , en , eo, et, ee, fo, fj, fi, nl, fr, ff, gd, gl, lg, ka
	// de , ki , el, kl, gn, gu, ht, ha, he, hz, hi, ho, hu, is, io
	// ig , id , ia, ie, iu, ik, ga, it, ja, jv, kl, kn, kr, ks, kk
	// ki , rw , ky, kv, kg, ko, kj, ku, kj, ky, lo, la, lv, lb, li
	// ln , lt , lu, lb, mk, mg, ms, ml, dv, mt, gv, mi, mr, mh, mo
	// mn , na , nv, nv, nd, nr, ng, ne, nd, se, no, nb, nn, ii, nn,
	// ie , oc , oj, cu, or, om, os, pi, pa, ps, fa, pl, pt, oc, pa,
	// ps , qu , ro, rm, rn, ru, sm, sa, sc, gd, sr, sn, ii, si, sk,
	// sl , so , st, nr, es, su, sw, ss, sv, tl, ty, tg, ta, tt, te, 
	// th , bo , ti, to, ts, tn, tr, tk, tw, ug, uk, ur, ug, uz, ca,
	// ve , vi , vo, wa, cy, fy, wo, xh, yi, yo, za, zu
	
	private boolean lang(VTDNavHuge vn, String s){
	    // check the length of s 
	    boolean b = false;
	    vn.push2();
        try {
            while (vn.getCurrentDepth() >= 0) {
                int i = vn.getAttrVal("xml:lang");
                if (i!=-1){
                    b = vn.matchTokenString(i,s);
                    break;                    
                }
                vn.toElement(VTDNavHuge.P);
            }
        } catch (NavExceptionHuge e) {

        }
	    vn.pop2();
	    return b;
	}
	private boolean startsWith(VTDNavHuge vn){
		String s2 = argumentList.next.e.evalString(vn);
		if (argumentList.e.isNodeSet()){
			//boolean b = false;
			int a = evalFirstArgumentListNodeSet(vn);
	        if (a==-1)
	        	return "".startsWith(s2);
	        else{
	        	try{
	        		return vn.startsWith(a, s2);
	        	}catch(Exception e){
	        	}
	        	return false;
	        }								
		} 
	    String s1 = argumentList.e.evalString(vn);
	    return s1.startsWith(s2); 
	}
	
	private boolean contains(VTDNavHuge vn){
		String s2 = argumentList.next.e.evalString(vn);
		if (argumentList.e.isNodeSet()){
			int a = evalFirstArgumentListNodeSet(vn);
			if (a==-1)
				return false;
			try {
				return vn.contains(a, s2);
			}catch (Exception e){
				return false;
			}				
		}		
	    String s1 = argumentList.e.evalString(vn);
	    
	    //return s1.contains(s2);
	    return s1.indexOf(s2)!=-1;
	    //return (s1.i))
	}
	
	private String subString(VTDNavHuge vn){
	    if (argCount()== 2){
	        String s = argumentList.e.evalString(vn);
	        double d1 = Math.floor(argumentList.next.e.evalNumber(vn)+0.5d);
	        if (d1!=d1 || d1>s.length())    
	            return "";
	        return s.substring(Math.max((int)(d1-1),0));
	    } else if (argCount() == 3){
	        String s = argumentList.e.evalString(vn);
            double d1 = Math.floor(argumentList.next.e.evalNumber(vn) + 0.5d);
            double d2 = Math
                    .floor(argumentList.next.next.e.evalNumber(vn) + 0.5d);
            //int i1 = Math.max(0, (int) d1 - 1);
            if ((d1 + d2) != (d1 + d2) || d1 > s.length())
                return "";
            return s.substring(Math.max(0, (int) d1 - 1), Math.min(s.length(),
                    (int) (d1 - 1) + (int) d2));
            //(int) argumentList.next.next.e.evalNumber(vn)-1);
	       
	    }
	    throw new IllegalArgumentException
		("substring()'s argument count is invalid");
	}
	private String subStringBefore(VTDNavHuge vn){
	    if (argCount()==2){
	        String s1 = argumentList.e.evalString(vn);
	        String s2 = argumentList.next.e.evalString(vn);
	        int len1 = s1.length();
	        int len2 = s2.length();
	        for (int i=0;i<len1;i++){
	            if (s1.regionMatches(i,s2,0,len2))
	                return s1.substring(0,i);
	        }
	        return "";
	    }
	    throw new IllegalArgumentException
		("substring()'s argument count is invalid");
	}
	private String subStringAfter(VTDNavHuge vn){
	    if (argCount()==2){
	        String s1 = argumentList.e.evalString(vn);
	        String s2 = argumentList.next.e.evalString(vn);
	        int len1 = s1.length();
	        int len2 = s2.length();
	        for (int i=0;i<len1;i++){
	            if (s1.regionMatches(i,s2,0,len2))
	                return s1.substring(i+len2);
	        }
	        return "";	        
	    }
	    throw new IllegalArgumentException
		("substring()'s argument count is invalid");
	}
	private String translate(VTDNavHuge vn)
	{
		int numArg = argCount();
		
	    if (numArg == 3)
	    {
	        String resultStr = argumentList.e.evalString(vn);
	        String indexStr = argumentList.next.e.evalString(vn);
	        
	        if(resultStr == null || resultStr.length() == 0 || indexStr == null || indexStr.length() == 0) return resultStr;
	        
	        String replace = argumentList.next.next.e.evalString(vn);
	        
	        
	        StringBuilder usedCharStr = new StringBuilder();
	        
	        
	        int lenRep = (replace != null)?replace.length() : 0;
	        
	        
	        for(int i = 0;i< indexStr.length(); i++)
	        {
	        	char idxChar = indexStr.charAt(i);
	        	
	        	if(usedCharStr.indexOf(String.valueOf(idxChar)) < 0)
	        	{
	        		
	        		if(i < lenRep)
	        		{
	        			resultStr = resultStr.replace(idxChar, replace.charAt(i));	        		
	        		}
	        		else
	        		{
	        			resultStr = resultStr.replaceAll(String.valueOf(idxChar), "");
	        		}
	        	
	        		usedCharStr.append(idxChar);
	        	
	        	}

	        
	        }
	        
	        return resultStr;
	        
	    }
	    else
	    {
	    	throw new IllegalArgumentException("Argument count for translate() is invalid. Expected: 3; Actual: " + numArg);
	    }
	}
	
	private String normalizeSpace(VTDNavHuge vn){
	    if (argCount()== 0){
	        String s =null;
	        try{
	            if (vn.atTerminal){
	                int ttype = vn.getTokenType(vn.LN);
	                if (ttype == VTDNavHuge.TOKEN_CDATA_VAL )
	                    s= vn.toRawString(vn.LN);
	                else if (ttype == VTDNavHuge.TOKEN_ATTR_NAME ||
	                         ttype == VTDNavHuge.TOKEN_ATTR_NS){
	                    s = vn.toNormalizedString(vn.LN+1);
	                } else
	                    s= vn.toNormalizedString(vn.LN);	                
	            }else {
	                s= vn.toNormalizedString(vn.getCurrentIndex());
	            }
	            return s;
	        }
	    	catch(NavExceptionHuge e){
	    	    return ""; // this will almost never occur
	    	}
	    } else if (argCount() == 1){
	    	String s="";
	    	if (argumentList.e.isNodeSet()){
				//boolean b = false;
				int a = evalFirstArgumentListNodeSet(vn);
		        if (a==-1)
		        	return ""; 
		        else {		        	
		        	try{
		        		s = vn.toNormalizedString(a); 
		        	} catch (Exception e){
		        	}
		        	return s;	
		        }	    	
	    	}
	    	else {
	    		s = argumentList.e.evalString(vn);
		        return normalize(s);
	    	}
	    }
	    throw new IllegalArgumentException
		("normalize-space()'s argument count is invalid");
	    //return null;
	}
	private String normalize(String s){
	    int len = s.length();
        StringBuffer sb = new StringBuffer(len);
        int i=0;
        // strip off leading ws
        for(i=0;i<len;i++){	            
            if (isWS(s.charAt(i))){
            }else{
                break;
            }
        }
        while(i<len){
            char c = s.charAt(i);
            if (!isWS(c)){
                sb.append(c);
                i++;
            } else {
                while(i<len){
                    c = s.charAt(i);
                    if (isWS(c))
                      i++;
                    else 
                        break;
                }
                if (i<len)
                  sb.append(' ');	                    
            }
        }
        return sb.toString();
	}
	
	private boolean isWS(char c){
	    if (c==' ' || c=='\t' || c=='\r'||c=='\n')
	        return true;
	    return false;
	}
	
	private String concat(VTDNavHuge vn){
	    StringBuffer  sb = new StringBuffer();
	    if (argCount()>=2){
			Alist temp = argumentList;
			while(temp!=null){
				sb.append(temp.e.evalString(vn));
				temp = temp.next;
			}
			return sb.toString();
	    } else 
	        throw new IllegalArgumentException
		("concat()'s argument count is invalid");
	}
	
	private String getString(VTDNavHuge vn){
	    if (argCount()== 0)
	        try{
	            if (vn.atTerminal){
	                if (vn.getTokenType(vn.LN) == VTDNavHuge.TOKEN_CDATA_VAL )
	                    return vn.toRawString(vn.LN);
	                return vn.toString(vn.LN);
	            }
	            return vn.toString(vn.getCurrentIndex());
	        }
	    	catch(NavExceptionHuge e){
	    	    return ""; // this will almost never occur
	    	}
	    else if (argCount() == 1){
	        return argumentList.e.evalString(vn);
	    } else 
	        throw new IllegalArgumentException
			("String()'s argument count is invalid");
	}
	
	public String evalString(VTDNavHuge vn) throws UnsupportedException{
	    //int d=0;
	  switch(opCode){
	  		case FuncName.CONCAT:
	  		    return concat(vn);
	  		    //throw new UnsupportedException("Some functions are not supported");
	  		    
			case FuncName.LOCAL_NAME:
			    return getLocalName(vn);

			case FuncName.NAMESPACE_URI: 
			    return getNameSpaceURI(vn);

			case FuncName.NAME: 		
			    return getName(vn);

			case FuncName.STRING:
			    return getString(vn);

			case FuncName.SUBSTRING_BEFORE:	return subStringBefore(vn);	
			case FuncName.SUBSTRING_AFTER: 	return subStringAfter(vn);
			case FuncName.SUBSTRING: 	return subString(vn);	
			case FuncName.TRANSLATE: 	return translate(vn);
			case FuncName.NORMALIZE_SPACE: return normalizeSpace(vn);
			//case FuncName.LANG: return lang(vn)
			case FuncName.CODE_POINTS_TO_STRING: 
				throw new com.ximpleware.extended.xpath.UnsupportedException("not yet implemented");			
			case FuncName.UPPER_CASE:return upperCase(vn);
			case FuncName.LOWER_CASE:return lowerCase(vn);
			case FuncName.QNAME:		
			case FuncName.LOCAL_NAME_FROM_QNAME:				
			case FuncName.NAMESPACE_URI_FROM_QNAME:				
			case FuncName.NAMESPACE_URI_FOR_PREFIX:				
			case FuncName.RESOLVE_QNAME:	
			case FuncName.IRI_TO_URI:    	
			case FuncName.ESCAPE_HTML_URI:	
			case FuncName.ENCODE_FOR_URI:
			    throw new com.ximpleware.extended.xpath.UnsupportedException("not yet implemented");
			default: if (isBoolean()){
			    		if (evalBoolean(vn)== true)
			    		    return "true";
			    		else 
			    		    return "false";
					 } else {
					     return ""+ evalNumber(vn);					     
					 }
	  }
	}	
	public double evalNumber(VTDNavHuge vn){
	    int ac = 0;
	  switch(opCode){
			case FuncName.LAST:  if (argCount()!=0 )
									throw new IllegalArgumentException
									("floor()'s argument count is invalid");
								 return contextSize;			
			case FuncName.POSITION:   if (argCount()!=0 )
									throw new IllegalArgumentException
									("position()'s argument count is invalid");
								 return position;
			case FuncName.COUNT: 	return count(vn);
			case FuncName.NUMBER:   if (argCount()!=1)
										throw new IllegalArgumentException
										("number()'s argument count is invalid");
									return argumentList.e.evalNumber(vn);
									
			case FuncName.SUM:	    return sum(vn);
			case FuncName.FLOOR: 	if (argCount()!=1 )
			    						throw new IllegalArgumentException("floor()'s argument count is invalid");
			    					return Math.floor(argumentList.e.evalNumber(vn));
			    					
			case FuncName.CEILING:	if (argCount()!=1 )
			    						throw new IllegalArgumentException("ceiling()'s argument count is invalid");
			    					return Math.ceil(argumentList.e.evalNumber(vn));
			    					
			case FuncName.STRING_LENGTH:
			    					ac = argCount();
			    					if (ac == 0){
			    					    try{
			    					        if (vn.atTerminal == true){
			    					            int type = vn.getTokenType(vn.LN);
			    					            if (type == VTDNavHuge.TOKEN_ATTR_NAME 
			    					                || type == VTDNavHuge.TOKEN_ATTR_NS){
			    					                return vn.getStringLength(vn.LN+1);
			    					            } else {
			    					                return vn.getStringLength(vn.LN);
			    					            }
			    					        }else {
			    					            int i = vn.getText();
			    					            if (i==-1)
			    					                return 0;
			    					            else 
			    					                return vn.getStringLength(i);
			    					        }
			    					    }catch (NavExceptionHuge e){
			    					        return 0;
			    					    }
			    					} else if (ac == 1){
			    					    return argumentList.e.evalString(vn).length();
			    					} else {
			    					    throw new IllegalArgumentException("string-length()'s argument count is invalid");
			    					}
			    
			case FuncName.ROUND: 	if (argCount()!=1 )
			    						throw new IllegalArgumentException("round()'s argument count is invalid");
			    					return Math.floor(argumentList.e.evalNumber(vn))+0.5d;
			    					
			case FuncName.ABS:		if (argCount() != 1)
	    		throw new IllegalArgumentException(
		    	"abs()'s argument count is invalid");
					return Math.abs(argumentList.e.evalNumber(vn));			
			case FuncName.ROUND_HALF_TO_EVEN :			    							
			case FuncName.ROUND_HALF_TO_ODD:
			    throw new com.ximpleware.extended.xpath.UnsupportedException("not yet implemented");
			    							
			
			default: if (isBoolean){
			    		if (evalBoolean(vn))
			    		    return 1;
			    		else
			    		    return 0;
					 }else {
					     try {
								double dval = Double.parseDouble(evalString(vn));
								return dval;
							}catch (NumberFormatException e){
								return Double.NaN;
							}				        
					 }
	  }
	}

	public int evalNodeSet(VTDNavHuge vn) throws XPathEvalExceptionHuge{
	  throw new XPathEvalExceptionHuge(" Function Expr can't eval to node set ");
	}
	
	public boolean evalBoolean(VTDNavHuge vn){
	  	  switch(opCode){
			case FuncName.STARTS_WITH:
			    if (argCount()!=2){
			        throw new IllegalArgumentException("starts-with()'s argument count is invalid");
			    }
			    return startsWith(vn);
			case FuncName.CONTAINS:
			    if (argCount()!=2){
			        throw new IllegalArgumentException("contains()'s argument count is invalid");
				}
			    return contains(vn);
			case FuncName.TRUE: if (argCount()!=0){
									throw new IllegalArgumentException("true() doesn't take any argument");
								}
								return true;			
			case FuncName.FALSE:if (argCount()!=0){
									throw new IllegalArgumentException("false() doesn't take any argument");
								}
								return false;	
			case FuncName.BOOLEAN: if (argCount()!=1){
										throw new IllegalArgumentException("boolean() doesn't take any argument");
								   }
									return argumentList.e.evalBoolean(vn);	
			case FuncName.NOT:	if (argCount()!=1){
										throw new IllegalArgumentException("not() doesn't take any argument");
			   					}
								return !argumentList.e.evalBoolean(vn);
		    case FuncName.LANG:
		        				if (argCount()!=1){
		        				    	throw new IllegalArgumentException("lang()'s argument count is invalid");
		        				}
								return lang(vn,argumentList.e.evalString(vn));
		    case FuncName.COMPARE:throw new com.ximpleware.extended.xpath.UnsupportedException("not yet implemented");
		    case FuncName.ENDS_WITH:
		    	if (argCount()!=2){
			        throw new IllegalArgumentException("starts-with()'s argument count is invalid");
			    }
			    return endsWith(vn);
			default: if (isNumerical()){
			    		double d = evalNumber(vn);
			    		if (d==0 || d!=d)
			    		    return false;
			    		return true;
					 }else{
					     return evalString(vn).length()!=0;
					 }			
		  }
	}
	
	public void reset(VTDNavHuge vn){
	    a = 0;
	    //contextSize = 0;
		if (argumentList!=null)
			argumentList.reset(vn);
	}

	public String fname(){
		switch(opCode){
			case FuncName.LAST: 			return "last";
			case FuncName.POSITION: 		return "position";
			case FuncName.COUNT: 			return "count";
			case FuncName.LOCAL_NAME: 		return "local-name";
			case FuncName.NAMESPACE_URI: 		return "namespace-uri";
			case FuncName.NAME: 			return "name";
			case FuncName.STRING: 			return "string";
			case FuncName.CONCAT: 			return "concat";
			case FuncName.STARTS_WITH:		return "starts-with";
			case FuncName.CONTAINS: 		return "contains";
			case FuncName.SUBSTRING_BEFORE: 	return "substring-before";
			case FuncName.SUBSTRING_AFTER: 		return "substring-after";
			case FuncName.SUBSTRING: 		return "substring";
			case FuncName.STRING_LENGTH: 		return "string-length";
			case FuncName.NORMALIZE_SPACE: 		return "normalize-space";
			case FuncName.TRANSLATE:	 	return "translate";
			case FuncName.BOOLEAN: 			return "boolean";
			case FuncName.NOT: 			return "not";
			case FuncName.TRUE: 			return "true";
			case FuncName.FALSE: 			return "false";
			case FuncName.LANG: 			return "lang";
			case FuncName.NUMBER:			return "number";
			case FuncName.SUM: 			return "sum";
			case FuncName.FLOOR: 			return "floor";
			case FuncName.CEILING: 			return "ceiling";
			case FuncName.ROUND:			return "round";
			case FuncName.ABS:				return "abs";
			case FuncName.ROUND_HALF_TO_EVEN :											
											return "round-half-to-even";
			case FuncName.ROUND_HALF_TO_ODD:
			    							return "round-half-to-odd";
			case FuncName.CODE_POINTS_TO_STRING:
			    							return "code-points-to-string";
			case FuncName.COMPARE:			return "compare";
			case FuncName.UPPER_CASE:		return "upper-case";
			case FuncName.LOWER_CASE:		return "lower-case";
			case FuncName.ENDS_WITH:		return "ends-with";
			case FuncName.QNAME:			return "qname";
			case FuncName.LOCAL_NAME_FROM_QNAME:
											return "local-name-from-QName";
			case FuncName.NAMESPACE_URI_FROM_QNAME:
											return "namespace-uri-from-QName";
			case FuncName.NAMESPACE_URI_FOR_PREFIX:
			    							return "namespace-uri-for-prefix";
			case FuncName.RESOLVE_QNAME:	return "resolve-QName";
			case FuncName.IRI_TO_URI:    	return "iri-to-uri";
			case FuncName.ESCAPE_HTML_URI:	return "escape-html-uri";
			default:						return "encode-for-uri";
		}
	}
	public boolean  isNodeSet(){
		return false;
	}

	public boolean  isNumerical(){
		return isNumerical;
	}
	
	public boolean isString(){
	    return isString;
	}
	
	public boolean isBoolean(){
	    return isBoolean;
	}
	
	private int count(VTDNavHuge vn){
	    int a = -1;
	    if (argCount()!=1 || argumentList.e.isNodeSet()==false)
			throw new IllegalArgumentException
				("Count()'s argument count is invalid");
		vn.push2();
		try{
			a = 0;
			argumentList.e.adjust(vn.getTokenCount());
			while(argumentList.e.evalNodeSet(vn)!=-1){
				a ++;
			}
			argumentList.e.reset(vn);
			vn.pop2();			
		}catch(Exception e){
			argumentList.e.reset(vn);
			vn.pop2();
		}
		return a;
	}
	
	private double sum(VTDNavHuge vn){
	    double d=0;
	    if (argCount() != 1 || argumentList.e.isNodeSet() == false)
	        throw new IllegalArgumentException("sum()'s argument count is invalid");
    	vn.push2();
    	try {
    	    a = 0;
    	    int i1;
    	    while ((a =argumentList.e.evalNodeSet(vn)) != -1) {
    	        int t = vn.getTokenType(a);
                if (t == VTDNavHuge.TOKEN_STARTING_TAG){
                    i1 = vn.getText();
                    if (i1!=-1)
                        d += vn.parseDouble(i1);
                    if (Double.isNaN(d))
                        break;
                }
                else if (t == VTDNavHuge.TOKEN_ATTR_NAME
                        || t == VTDNavHuge.TOKEN_ATTR_NS){
                    d += vn.parseDouble(a+1);
                    if (Double.isNaN(d))
                        break;
                }
                else if (t == VTDNavHuge.TOKEN_CHARACTER_DATA
                        || t == VTDNavHuge.TOKEN_CDATA_VAL){
                    d += vn.parseDouble(a);
                    if (Double.isNaN(d))
                        break;
                }
                //    fib1.append(i);
    	    }
    	    argumentList.e.reset(vn);
    	    vn.pop2();
    	    return d;
    	} catch (Exception e) {
    	    argumentList.e.reset(vn);
    	    vn.pop2();
    	    return Double.NaN;
    	}
	    
	}
	// to support computer context size 
	// needs to add 
	
	public boolean requireContextSize(){
	    if (opCode == FuncName.LAST)
	        return true;
	    else {
	        Alist temp = argumentList;
	        //boolean b = false;
	        while(temp!=null){
	            if (temp.e.requireContextSize()){
	                return true;
	            }
	            temp = temp.next;
	        }
	    }
	    return false;
	}
	
	public void setContextSize(int size){	
	    if (opCode == FuncName.LAST){
	        contextSize = size;
	        //System.out.println("contextSize: "+size);
	    } else {
	        Alist temp = argumentList;
	        //boolean b = false;
	        while(temp!=null){
	            temp.e.setContextSize(size);
	            temp = temp.next;
	        }
	    }
	}
	
	public void setPosition(int pos){
	    if (opCode == FuncName.POSITION){
	        position = pos;
	        //System.out.println("PO: "+size);
	    } else {
	        Alist temp = argumentList;
	        //boolean b = false;
	        while(temp!=null){
	            temp.e.setPosition(pos);
	            temp = temp.next;
	        }
	    }
	}
	public int adjust(int n){
	    int i = 0;
	    switch(opCode){
	    	case FuncName.COUNT: 
	        case FuncName.SUM:
	            i = argumentList.e.adjust(n);
	    		break;
	    	default: 
	    }
	    return i;
	}
	
	private int evalFirstArgumentListNodeSet(VTDNavHuge vn){
		vn.push2();
        int size = vn.contextStack2.size;
        int a = -1;
        try {
            a = argumentList.e.evalNodeSet(vn);
            if (a != -1) {
                if (vn.getTokenType(a) == VTDNavHuge.TOKEN_ATTR_NAME) {
                    a++;
                }
                if (vn.getTokenType(a) == VTDNavHuge.TOKEN_STARTING_TAG) {
                    a = vn.getText();
                }
            }	            
        } catch (Exception e) {
        }
        vn.contextStack2.size = size;
        argumentList.e.reset(vn);
        vn.pop2();
        return a;
	}
	
	private int evalFirstArgumentListNodeSet2(VTDNavHuge vn){
		vn.push2();
        int size = vn.contextStack2.size;
        int a = -1;
        try {
            a = argumentList.e.evalNodeSet(vn);	            
        } catch (Exception e) {
        }
        vn.contextStack2.size = size;
        argumentList.e.reset(vn);
        vn.pop2();
        return a;
	}
	
	private String upperCase(VTDNavHuge vn){
		if (argCount()==1){
			if (argumentList.e.isNodeSet()){
				int a = evalFirstArgumentListNodeSet(vn);
		        if (a==-1)
		        	return "";
		        else{
		        	try{
		        		return vn.toStringUpperCase(a);
		        	}catch(Exception e){
		        	}
		        	return "";
		        }		
			}else {
				return (argumentList.e.evalString(vn)).toUpperCase();
			}
		}else 
			throw new IllegalArgumentException
			("upperCase()'s argument count is invalid");
		
	}
	
	private String lowerCase(VTDNavHuge vn){
		if (argCount()==1){
			if (argumentList.e.isNodeSet()){
				int a = evalFirstArgumentListNodeSet(vn);
		        if (a==-1)
		        	return "";
		        else{
		        	try{
		        		return vn.toStringLowerCase(a);
		        	}catch(Exception e){
		        	}
		        	return "";
		        }		
			}else {
				return (argumentList.e.evalString(vn)).toLowerCase();
			}
		}else 
			throw new IllegalArgumentException
			("lowerCase()'s argument count is invalid");
	}
	
	private boolean endsWith(VTDNavHuge vn){
		String s2 = argumentList.next.e.evalString(vn);
		if (argumentList.e.isNodeSet()){
			int a = evalFirstArgumentListNodeSet(vn);
	        if (a==-1)
	        	return "".startsWith(s2);
	        else{
	        	try{
	        		return vn.endsWith(a, s2);
	        	}catch(Exception e){
	        	}
	        	return false;
	        }								
		}	
	    String s1 = argumentList.e.evalString(vn);
	    return s1.endsWith(s2); 
	}

}