/*
 * Copyright (C) 2005 NTT DATA Corporation
 * 
 */
package org.postgresforest.vm;

import java.io.IOException;
import java.io.StreamTokenizer;
import java.io.StringReader;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

import org.postgresforest.Driver;
import org.postgresforest.core.ParameterList;
import org.postgresforest.util.GT;
import org.postgresforest.util.PSQLException;
import org.postgresforest.vm.err.ForestSQLState;

/**
 * SQL構文解析クラス.
 * SQL文の構文解析の実行し、コマンドタイプを判別。
 * 各コマンドタイプにあわせて、テーブル、ＷＨＥＲＥ句（カラム−値）などを取り出す。
 * 
 *
 */

public class Parser {

	//コマンド・キーワードID
	public static final int NONE = -1; //解析不可

	public static final int CALL = 1;
	public static final int CHECKPOINT = 2;
	public static final int COMMIT = 3;
	public static final int CONNECT = 4;
	public static final int CREATE = 5;
	public static final int DELETE = 6;
	public static final int DISCONNECT = 7;
	public static final int DROP = 8;
	public static final int GRANT = 9;
	public static final int INSERT = 10;
	public static final int REVOKE = 11;
	public static final int ROLLBACK = 12;
	public static final int SAVEPOINT = 13;
	public static final int SCRIPT = 14;
	public static final int SELECT = 15;
	public static final int SET = 16;
	public static final int SHUTDOWN = 17;
	public static final int UPDATE = 18;
	public static final int SEMICOLON = 19;
	public static final int ALTER = 20;
	public static final int ADD = 24;
	public static final int ALIAS = 35;
	public static final int AUTOCOMMIT = 43;
	public static final int CACHED = 31;
	public static final int COLUMN = 27;
	public static final int CONSTRAINT = 25;
	public static final int FOREIGN = 26;
	public static final int IGNORECASE = 41;
	public static final int INDEX = 22;
	public static final int LOGSIZE = 39;
	public static final int LOGTYPE = 40;
	public static final int MAXROWS = 42;
	public static final int MEMORY = 30;
	public static final int PASSWORD = 37;
	public static final int PRIMARY = 36;
	public static final int READONLY = 38;
	public static final int REFERENTIAL_INTEGRITY = 46;
	public static final int RENAME = 23;
	public static final int SOURCE = 44;
	public static final int FROM = 101;
	public static final int WHERE = 102;
	public static final int GROUP = 103; //GROUP BY
	public static final int HAVING = 104;
	public static final int UNION = 105;
	public static final int INTERSECT = 106;
	public static final int EXCEPT = 107;
	public static final int ORDER = 108; //ORDER BY
	public static final int LIMIT = 109;
	public static final int OFFSET = 110;
	public static final int FOR = 111; //FOR UPDATE

	public static final int AS = 112;
	public static final int INNER = 113;
	public static final int OUTER = 114;
	public static final int RIGHT = 115;
	public static final int LEFT = 116;
	public static final int JOIN = 117;

	public static final int AND = 118;
	public static final int OR = 119;
	public static final int IN = 120;
	public static final int NOT = 121;
	public static final int ONLY = 122;
	public static final int INTO = 123;
	public static final int VALUES = 124;
	public static final int DEFAULT = 125;

	public static final int COUNT = 126;
	public static final int AGV = 127;
	public static final int MAX = 128;
	public static final int MIN = 129;
	public static final int STDDEV = 130;
	public static final int SUM = 131;
	public static final int VARIANCE = 132;
	public static final int ON = 133;
	public static final int LIKE = 134;

	public static final int CAST = 135;

	//Ver1.1 FROMの読み飛ばし用
	public static final int SUBSTRING = 136;
	public static final int TRIM = 137;
	public static final int EXTRACT = 138;
	public static final int SUBSTR = 139;
	public static final int OVERLAY = 140;
	
	//Ver1.1 DISTINCTはViewで処理
	public static final int DISTINCT = 141;
	

	//Ver2.0
	public static final int ASC = 142;
	public static final int DESC = 143;
	public static final int USING = 144;

	//Ver1.1R4
	public static final int BY = 145;
	
	//Ver2.0
	public static final int NATURAL = 146;
	public static final int FULL = 147;
	
	//Ver3.0
	public static final int IS = 148;
	
	//Ver3.2 集約関数追加
	public static final int BIT_AND = 149;
	public static final int BIT_OR = 150;
	public static final int BOOL_AND = 151;
	public static final int BOOL_OR = 152;
	public static final int EVERY = 153;
	
	
	
	

	
	/** コマンド・キーワードIDと文字列マッピングテーブル */
	protected static final CommandMap commandSet = new CommandMap();

	//
	protected ArrayList m_paramList = new ArrayList();
	
	
	// SQLコマンド判別用のマップ初期化

	static {

		commandSet.put("alter", ALTER);
		commandSet.put("call", CALL);
		commandSet.put("checkpoint", CHECKPOINT);
		commandSet.put("commit", COMMIT);
		commandSet.put("connect", CONNECT);
		commandSet.put("create", CREATE);
		commandSet.put("delete", DELETE);
		commandSet.put("disconnect", DISCONNECT);
		commandSet.put("drop", DROP);
		commandSet.put("grant", GRANT);
		commandSet.put("insert", INSERT);
		commandSet.put("revoke", REVOKE);
		commandSet.put("rollback", ROLLBACK);
		commandSet.put("savepoint", SAVEPOINT);
		commandSet.put("script", SCRIPT);
		commandSet.put("select", SELECT);
		commandSet.put("set", SET);
		commandSet.put("shutdown", SHUTDOWN);
		commandSet.put("update", UPDATE);
		commandSet.put(";", SEMICOLON);
		//

		commandSet.put("index", INDEX);
		commandSet.put("rename", RENAME);
		commandSet.put("add", ADD);
		commandSet.put("constraint", CONSTRAINT);
		commandSet.put("foreign", FOREIGN);
		commandSet.put("column", COLUMN);
		commandSet.put("memory", MEMORY);
		commandSet.put("cached", CACHED);
		commandSet.put("alias", ALIAS);
		commandSet.put("primary", PRIMARY);
		commandSet.put("password", PASSWORD);
		commandSet.put("readonly", READONLY);
		commandSet.put("logsize", LOGSIZE);
		commandSet.put("logtype", LOGTYPE);
		commandSet.put("ignorecase", IGNORECASE);
		commandSet.put("maxrows", MAXROWS);
		commandSet.put("autocommit", AUTOCOMMIT);
		commandSet.put("source", SOURCE);
		commandSet.put("referential_integrity", REFERENTIAL_INTEGRITY);
		commandSet.put("from", FROM);
		commandSet.put("where", WHERE);
		commandSet.put("group", GROUP);
		commandSet.put("having", HAVING);
		commandSet.put("union", UNION);
		commandSet.put("intersect", INTERSECT);
		commandSet.put("except", EXCEPT);
		commandSet.put("order", ORDER);
		commandSet.put("limit", LIMIT);
		commandSet.put("offset", OFFSET);
		commandSet.put("for", FOR);
		commandSet.put("as", AS);

		commandSet.put("inner", INNER);
		commandSet.put("outer", OUTER);
		commandSet.put("right", RIGHT);
		commandSet.put("left", LEFT);
		commandSet.put("join", JOIN);

		commandSet.put("and", AND);
		commandSet.put("or", OR);
		commandSet.put("in", IN);
		commandSet.put("not", NOT);
		commandSet.put("only", ONLY);
		commandSet.put("into", INTO);
		commandSet.put("values", VALUES);
		commandSet.put("default", DEFAULT);

		commandSet.put("count", COUNT);
		commandSet.put("avg", AGV);
		commandSet.put("max", MAX);
		commandSet.put("min", MIN);
		commandSet.put("stddev", STDDEV);
		commandSet.put("sum", SUM);
		commandSet.put("variance", VARIANCE);
		commandSet.put("on", ON);
		commandSet.put("like", LIKE);

		commandSet.put("cast", CAST);
		
		//Ver1.1
		commandSet.put("substring", SUBSTRING);
		commandSet.put("trim", TRIM);
		commandSet.put("extract", EXTRACT);
		commandSet.put("substr", SUBSTR);
		commandSet.put("overlay", OVERLAY);

		commandSet.put("distinct", DISTINCT);

		//Ver2.0
		commandSet.put("asc", ASC);
		commandSet.put("desc", DESC);
		commandSet.put("using", USING);

		//Ver1.1R4
		commandSet.put("by", BY);

		//Ver2.0
		commandSet.put("natural", NATURAL);
		commandSet.put("full", FULL);
		
		//Ver3.0
		commandSet.put("is", IS);
		
		//Ver3.2
		commandSet.put("bit_and", BIT_AND);
		commandSet.put("bit_or", BIT_OR);
		commandSet.put("bool_and", BOOL_AND);
		commandSet.put("bool_or", BOOL_OR);
		commandSet.put("every", EVERY);
		
		
		
	}


	/** コマンドタイプ */
	protected int m_type 				= NONE;

	/** テーブルリスト */
	protected ArrayList m_tables; 

	/** INSERT・SETカラム*/
	protected ArrayList m_updateCol; 

	/** 構文解析用トークナイザ*/
	protected SqlTokenizer m_tokenizer;

	/** ORDER　BY句の設定　*/
	protected boolean m_orderBy 		= false;
	/** GROUP　BY句の設定　*/
	protected boolean m_groupBy 		= false; 
	/** 集約関数（VIEWで処理する）の設定　*/
	protected boolean m_Function 	= false; 
	/** 結合が行われているか　*/
	protected boolean m_JoinTable 	= false; 
	/** ロックを行う　@since 1.1*/
	protected boolean m_ForUpdate	= false; 
	/** DISTINCTが指定　@since 1.1*/
	protected boolean m_distinct 	= false; 
	/** 集合演算子（UNION,INTERSECT,EXCEPT）が指定　@since 1.1*/
	protected boolean m_union 	    = false; 

	/** LIMITが指定　@since 2.0*/
	protected boolean m_limit 	    = false; 
	/** LIMITの指定行数　@since 2.0*/
	protected int m_limitCount 	    = 0; 
	/** OFFSETが指定　@since 2.0*/
	protected boolean m_offset 	    = false; 

	/** ORDER　BY句の設定　@since 2.0 */
	protected ArrayList m_orderList;

	/** GROUP　BY句の設定　@since 2.0 */
	protected ArrayList m_groupList;

	/** SELECT 出力列　@since 2.0 */
	protected ArrayList m_selectList;

	/** 集約関数（集計対象）の設定　@since 2.0 */
	protected boolean m_execFunction	= false; 

	/** HAVINGが指定　@since 3.0 */
	protected boolean m_having		= false; 

	
	/** 元SQL文 */
	protected String m_srcSql;

	
	/** ログ出力 @since 2.1 */
	protected LogUtil m_logUtil;

	/**
	 * 
	 */
	public Parser(LogUtil logUtil) {

		m_logUtil = logUtil;

		

	}

	/**
     * 
     * @since 
     */
    protected void init() {

        if(m_tables == null){
            m_tables = new ArrayList();
        }else{
            m_tables.clear();
        
        }

        if(m_updateCol == null){
    		m_updateCol = new ArrayList();
        }else{
        	m_updateCol.clear();
        
        }
        if(m_orderList == null){
    		m_orderList = new ArrayList();
        }else{
        	m_orderList.clear();
        
        }
        if(m_groupList == null){
    		m_groupList = new ArrayList();
        }else{
        	m_groupList.clear();
        
        }
        if(m_selectList == null){
    		m_selectList = new ArrayList();
        }else{
        	m_selectList.clear();
        
        }



    }
	
	
	/**
	 * 解析実行
	 * @param sql - SQL文
	 * @throws SQLException
	 */
	public void parse(String sql) throws SQLException{

	    //メンバ初期化
	    init();
	    m_srcSql = sql;

	    
		//トークナイザーの設定
		initTokenizer(sql);


		//コマンドタイプ取得
		try {
			/*
			 * 最初に出現する文字トークンを検出する
			 */
			while( !checkToken(StreamTokenizer.TT_WORD) ){
				if(m_tokenizer.ttype == StreamTokenizer.TT_EOF)
					break;
			}
		} catch (IOException e) {
			//構文解析エラー
			throw new PSQLException(GT.tr("SQL analysis error."),ForestSQLState.INTERNAL_ERROR, e);
		}

		//Ver1.1 空のSQLに対応
		//m_type = commandSet.getInt(m_tokenizer.sval);
		if(m_tokenizer.sval != null){
			m_type = commandSet.getInt(m_tokenizer.sval);
		}


		//コマンド別に構文解析を割り振る
		try {
			switch (m_type) {

				case SELECT :

					parseSelect();
					break;

				case INSERT :
					//次にINTOがくるかチェック Ver3.0
					if(checkToken(StreamTokenizer.TT_WORD)){
						int wtype = commandSet.getInt(m_tokenizer.sval);
						if( wtype != INTO) {			
						    throw new PSQLException(GT.tr("SQL analysis error."), ForestSQLState.INTERNAL_ERROR);
						}
					}
					parseInsert();
					break;

				case DELETE :

					parseDelete();
					break;

				case UPDATE :

					parseUpdate();
					break;

				default :
					//パラメータ取得などのため、最後までトークナイザーをまわす
					while (
							(m_tokenizer.nextToken()) != StreamTokenizer.TT_EOF) {
					}
					break;
			}

		} catch (IOException e1) {
			//構文解析エラー
			throw new PSQLException(GT.tr("SQL analysis error."), ForestSQLState.INTERNAL_ERROR, e1);
		}
		//Ver2.0 ログ追加
		if (Driver.logInfo){
			//コマンドタイプ
			switch (m_type) {
				case SELECT :
					m_logUtil.info("Parser Result: Command Type = SELECT");
					break;

				case INSERT :
					m_logUtil.info("Parser Result: Command Type = INSERT");
					break;

				case DELETE :
					m_logUtil.info("Parser Result: Command Type = DELETE");
					break;

				case UPDATE :
					m_logUtil.info("Parser Result: Command Type = UPDATE");
					break;
			}
			
			//抽出テーブル名
			String tableName = "";
			for (int i = 0; i < m_tables.size(); i++){
			    TableInfo tblInfo = (TableInfo)m_tables.get(i);
				tableName = tableName + tblInfo.getTableName() + ",";
			}
			m_logUtil.info("Parser Result: Extraction Table Name = " + tableName);

			//抽出更新カラム名
			String updateColumn = "";
			for (int i = 0; i < m_updateCol.size(); i++){
			    UpdateColumnInfo colInf = (UpdateColumnInfo)m_updateCol.get(i);
				updateColumn = updateColumn + colInf.getName() + ",";
			}
			m_logUtil.info("Parser Result: Extraction Update Column Name = " + updateColumn);
		
			//ORDER BY句
			m_logUtil.info("Parser Result: ORDER BY = " + m_orderBy);
			//GROUP BY句
			m_logUtil.info("Parser Result: GROUP BY = " + m_groupBy);
			//集約関数
			m_logUtil.info("Parser Result: FUNCTION = " + m_Function);
			//テーブル結合
			m_logUtil.info("Parser Result: JOIN = " + m_JoinTable);
			//ロックを行う
			m_logUtil.info("Parser Result: FOR UPDATE = " + m_ForUpdate);
			//DISTINCT指定
			m_logUtil.info("Parser Result: DISTINCT = " + m_distinct);
			//集合演算子（UNION,INTERSECT,EXCEPT）指定
			m_logUtil.info("Parser Result: UNION = " + m_union);
			//LIMIT指定
			m_logUtil.info("Parser Result: LIMIT = " + m_limit);
			//LIMITの指定行数
			m_logUtil.info("Parser Result: LIMIT COUNT = " + m_limitCount);
			//OFFSET指定
			m_logUtil.info("Parser Result: OFFSET = " + m_offset);
			
			//ORDER BY句の設定
			String orderName = "";
			for (int i = 0; i < m_orderList.size(); i++){
			    OrderInfo orderInfo = (OrderInfo)m_orderList.get(i);
			    orderName = orderName + orderInfo.getName() + ",";
			}
			m_logUtil.info("Parser Result: ORDER LIST = " + orderName);
			
			//GROUP BY句の設定
			String columnName = "";
			for (int i = 0; i < m_groupList.size(); i++){
				ColumnInfo columnInfo = (ColumnInfo)m_groupList.get(i);
				columnName = columnName + columnInfo.getName() + ",";
			}
			m_logUtil.info("Parser Result: GROUP LIST = " + columnName);
			
			//SELECT 出力列
			String selectColumnName = "";	
			for (int i = 0; i < m_selectList.size(); i++){
				SelectColumnInfo selectColumnInfo = (SelectColumnInfo)m_selectList.get(i);
				selectColumnName = selectColumnName + selectColumnInfo.getName() + ",";
			}	
			m_logUtil.info("Parser Result: SELECT LIST = " + selectColumnName);
			
			//集約関数（集計対象）の設定
			m_logUtil.info("Parser Result: FUNCTION(TOTAL) = " + m_execFunction);
		}


	}

	protected void initTokenizer(String sql) {
		//StreamTokenizerの初期化
		StringReader fr = new StringReader(sql);
		m_tokenizer = new SqlTokenizer(fr);
		
		//Ver1.1R4
		m_tokenizer.resetSyntax();
		m_tokenizer.wordChars('a', 'z');
		m_tokenizer.wordChars('A', 'Z');
		m_tokenizer.wordChars('_', '_');
		m_tokenizer.wordChars('.', '.');
		m_tokenizer.wordChars('0', '9');		
		
		m_tokenizer.ordinaryChar('/');//BUG '/'があると文字列終了とみなしていたので、文字に追加
		
		m_tokenizer.eolIsSignificant(true);
	}

	/**
	 * SELECT文構文解析 
	 * 
	 * @throws IOException - SQL分読み込みエラー（トークナイザー）
	 * @throws SQLException - 構文解析エラー
	 */
	protected void parseSelect() throws IOException, SQLException {

		int token = StreamTokenizer.TT_EOF;

		//Ver1.1 DISTINCT検出
		checkDistinct();
		
		//Ver2.0
		if(!m_distinct){
			m_tokenizer.pushBack();
		}
		
		int columnIndex = 1;		
		SelectColumnInfo selectColumnInfo = new SelectColumnInfo(columnIndex);
		m_selectList.add(selectColumnInfo);
		
		while (
			(token = m_tokenizer.nextToken()) != StreamTokenizer.TT_EOF) {

			if (token == StreamTokenizer.TT_WORD) {

				int wtype = commandSet.getInt(m_tokenizer.sval);

				String tmpSval = m_tokenizer.sval;//Vwe2.0

				switch (wtype) {

					//集約関数の検出
					case COUNT:
						if(checkToken('(')){
							//DISTINCT検出
							checkDistinct();
							
							m_execFunction = true;
							selectColumnInfo.setFunctionType(SelectColumnInfo.FUNCTION_COUNT);
							forwardPparenthesis();
							
						}else{
							selectColumnInfo.setName(tmpSval);
							m_tokenizer.pushBack();
						}
						break;					
					case MAX:
						if(checkToken('(')){
							m_execFunction = true;
							selectColumnInfo.setFunctionType(SelectColumnInfo.FUNCTION_MAX);
							forwardPparenthesis();
						}else{
							selectColumnInfo.setName(tmpSval);
							m_tokenizer.pushBack();
						}
						break;					
					case MIN:
						if(checkToken('(')){
							m_execFunction = true;
							selectColumnInfo.setFunctionType(SelectColumnInfo.FUNCTION_MIN);
							forwardPparenthesis();
						}else{
							selectColumnInfo.setName(tmpSval);
							m_tokenizer.pushBack();
						}
						break;					
					case SUM:
						if(checkToken('(')){
							//DISTINCT検出
							checkDistinct();

							m_execFunction = true;
							selectColumnInfo.setFunctionType(SelectColumnInfo.FUNCTION_SUM);
							forwardPparenthesis();
						}else{
							selectColumnInfo.setName(tmpSval);
							m_tokenizer.pushBack();
						}
						break;					



					//Ver3.2
					case BIT_AND:
					case BIT_OR:
					case BOOL_AND:
					case BOOL_OR:
					case EVERY:
						
					case AGV:
					case STDDEV:
					case VARIANCE:
						//040129 MOD カラム名と関数名が同じ場合に関数名と判断してしまうので後ろに'('があるとき関数と判断するように編集 
						//m_Function = true;
						//Ver1.1R4
						//token = m_tokenizer.nextToken();
						//if(token == '('){
						if(checkToken('(')){
							forwardPparenthesis();//Ver2.0
							m_Function = true;
						}else{
							selectColumnInfo.setName(tmpSval);//Ver2.0
							m_tokenizer.pushBack();
						}
						//040129 MOD
						break;					
					
					case FROM :
						//テーブルの抽出
						parseFrom();
						break;

					case WHERE :
						//WHERE句の抽出
						parseWhere();
						break;

					case GROUP :
						//GROUP BYの設定
						//Ver1.1R4token = m_tokenizer.nextToken(); //BY の読み飛ばし
						m_groupBy = true;
						parseGroupBy();
						break;

					case ORDER :
						//ORDER BYの設定	//Ver2.0
						//Ver1.1R4token = m_tokenizer.nextToken(); //BY の読み飛ばし
						m_orderBy = true;
						parseOrderBy();
						break;


					case UNION :
					case INTERSECT :
					case EXCEPT :
						//Ver1.1 集合演算子はViewで処理
						m_union = true;
						//パラメータ取得などのため、最後までトークナイザーをまわす
						while (
								(m_tokenizer.nextToken()) != StreamTokenizer.TT_EOF) {
						}
						return;
					
					case LIMIT :
						//Ver2.0
						m_limit = true; 
						if (Driver.logDebug)
						    m_logUtil.debug("LIMIT is specified");
						if(checkToken(StreamTokenizer.TT_WORD) &&
						   isNumeric(m_tokenizer.sval)){
								m_limitCount = new Integer( m_tokenizer.sval ).intValue();
								if (Driver.logDebug)
								    m_logUtil.debug("LIMIT Line:" + m_limitCount);
						}else{
							//構文解析エラー
							throw new PSQLException(GT.tr("SQL analysis error."), ForestSQLState.INTERNAL_ERROR);
						}
						break;
					
					case OFFSET :
						//Ver2.0 分散処理（VIEW）にする
						m_offset = true; 
						break;

					case HAVING :
						//Ver3.1
						m_having = true;
						break;

					case INTO :	//Ver1.1 SELECT * INTO を処理しない
						//SELECT INTO が指定されています。
						throw new PSQLException(GT.tr("SELECT INTO is specified."), ForestSQLState.INTERNAL_ERROR);


					case FOR :	//Ver1.1 FOR UPDATE
						//次にUPDATEがきたら FOR UPDATE
						if(checkToken(StreamTokenizer.TT_WORD)){
							wtype = commandSet.getInt(m_tokenizer.sval);
							if( wtype == UPDATE) {
								m_ForUpdate = true;						
								if (Driver.logDebug)
								    m_logUtil.debug("FOR UPDATE is specified！");
							}
						}
						break;

					case SELECT :
						//副問い合わせが指定されています。
						//Ver1.1 副問い合わせは括弧でくくられているので、対応する括弧まで読み飛ばし
						forwardPparenthesis();

						break;


					case SUBSTRING :
					case TRIM :
					case EXTRACT :
					case SUBSTR :
					case OVERLAY :
						//Ver1.1 文法上FROMが指定される関数の読み飛ばし
						//一応、関数のネストを考慮しているが、単に関数の階層を意識しているだけ
						//改良の余地あり
						if(checkToken('(')){
							forwardPparenthesis();
						}else{
							selectColumnInfo.setName(tmpSval);//Ver2.0
							m_tokenizer.pushBack();
						}
						
						break;

					case AS :
						//Ver2.0
						if( checkToken(StreamTokenizer.TT_WORD) ){
							selectColumnInfo.setAlias(m_tokenizer.sval);
						}else if(m_tokenizer.ttype == '"'){
							//"でくくられていたら、"がくるまでを文字列とする
							StringBuffer value = new StringBuffer();

							while ( (token = m_tokenizer.nextToken()) != StreamTokenizer.TT_EOF) {
		
								if(token == StreamTokenizer.TT_WORD ){
									value.append(m_tokenizer.sval); 
								}else if(token == '"'){
									selectColumnInfo.setAlias(value.toString());
									break;		
								}else{
									value.append((char)m_tokenizer.ttype); 
								}
		
							}

						}else{
							//構文解析エラー
							throw new PSQLException(GT.tr("SQL analysis error."), ForestSQLState.INTERNAL_ERROR);
						}
						break;
						
					case IS :	//Ver3.0 IS DISTINCT FROM
						//次にDISTINCTがきたら IS DISTINCT FROM
						if(checkToken(StreamTokenizer.TT_WORD)){
							wtype = commandSet.getInt(m_tokenizer.sval);
							if( wtype == DISTINCT) {
								//IS DISTINCT FROMが指定されています。
							    throw new PSQLException(GT.tr("IS DISTINCT FROM is specified."), ForestSQLState.INTERNAL_ERROR);
							}
						}
						break;
						
					default :
						//Ver2.0
						if(checkToken('(')){
							//関数なら読み飛ばし
							forwardPparenthesis();
							
							//Ver3.0 関数もリライト対象とする。
							//m_Function = true;//関数の動作がわからないので、VIEWとなるように
						}else{						
							selectColumnInfo.setName(tmpSval);
							m_tokenizer.pushBack();
						}
						break;

				}

			}
			//Ver2.0
			else if(token == ','){
				columnIndex++;
				selectColumnInfo = new SelectColumnInfo(columnIndex);
				m_selectList.add(selectColumnInfo);
			}else if(token == '*'){
				selectColumnInfo.setName("*");
			}else if(token == '('){
				forwardPparenthesis();
			}
			//Ver3.2 コメント対応
			else if(token == '-'){	
				forwardLineComment();
			}else if(token == '/'){
				forwardComment();
			}

		}

		//集約関数チェック
		//関数指定の場合、カラム名に値が入らない。
		//もし入っている場合は式が入力されているので、VIEWへの処理とするため関数フラグにtrueをセット
		for (int i = 0; i < m_selectList.size(); i++) {
			selectColumnInfo = (SelectColumnInfo)m_selectList.get(i);
			
			if(selectColumnInfo.getName() != null &&
			   selectColumnInfo.getFunctionType() != SelectColumnInfo.FUNCTION_NONE){
				m_Function = true;			   
				break;			   
			}
			
		}
		

	}

	/**
	 * DISTINCT チェック.
	 * 構文の次の単語がDISTINCTの場合、DISTINCTしてフラグをtrueにする
	 * 
	 * @throws IOException
	 * @since 1.1
	 */
	protected void checkDistinct() throws IOException {

		
		if(checkToken(StreamTokenizer.TT_WORD)){
			int wtype = commandSet.getInt(m_tokenizer.sval);
			if(wtype == DISTINCT){
				m_distinct = true;
			}
		}
		
	}

	/**
	 * INSERT文構文解析 
	 * 
	 * @throws IOException - SQL分読み込みエラー（トークナイザー）
	 * @throws SQLException - 構文解析エラー
	 */
	protected void parseInsert() throws IOException,SQLException{

		int token = StreamTokenizer.TT_EOF;
		boolean values = false; //Ver3.0 VALUES指定チェックの為
		
		while (
			(token = m_tokenizer.nextToken()) != StreamTokenizer.TT_EOF) {

			if (token == StreamTokenizer.TT_WORD) {

				int wtype = commandSet.getInt(m_tokenizer.sval);

				switch (wtype) {

					case INTO :
						break;

					case VALUES :
					    values = true; //Ver3.0 VALUES指定チェックの為
						parseValues();
						break;

					case SELECT :
						//INSERT SELECTが指定されています。
						throw new PSQLException(GT.tr("INSERT SELECT is specified."), ForestSQLState.INTERNAL_ERROR);
						//m_tables.clear();

					default :
					
						//テーブル
						TableInfo tblInfo = new TableInfo();
						tblInfo.setTableName(m_tokenizer.sval);
						m_tables.add(tblInfo);
					
						break;
				}

			}else if(token == '('){
				parseColumns();

			}
			//Ver3.2 コメント対応
			else if(token == '-'){	
				forwardLineComment();
			}else if(token == '/'){
				forwardComment();
			}

		}
		
		//Ver3.0 VALUES指定チェック
		if(!values){
			//構文解析エラー
		    throw new PSQLException(GT.tr("SQL analysis error."), ForestSQLState.INTERNAL_ERROR);
		}
	}

	/**
	 * UPDATE文構文解析 
	 * 
	 * @throws IOException - SQL分読み込みエラー（トークナイザー）
	 * @throws SQLException - 構文解析エラー
	 */
	protected void parseUpdate() throws IOException,SQLException{

		int token = StreamTokenizer.TT_EOF;

		while (
			(token = m_tokenizer.nextToken()) != StreamTokenizer.TT_EOF) {

			if (token == StreamTokenizer.TT_WORD) {

				int wtype = commandSet.getInt(m_tokenizer.sval);

				switch (wtype) {
					
					case FROM :
						//テーブルの抽出
						parseFrom();
						break;

					case WHERE :
						//WHERE句の抽出
						parseWhere();
						break;

					case ONLY :

						break;

					case SET :
						//SET句の抽出
						parseSet();
						break;
						

					case SELECT :
						//副問い合わせが指定されています。
						//throw new PSQLException("postgresql.forest.paser.subquery");

					default :
					
						//テーブル
						TableInfo tblInfo = new TableInfo();
						tblInfo.setTableName(m_tokenizer.sval);
						m_tables.add(tblInfo);
					
						break;

				}

			}
			//Ver3.2 コメント対応
			else if(token == '-'){	
				forwardLineComment();
			}else if(token == '/'){
				forwardComment();
			}

		}

	}

	/**
	 * DELETE文構文解析 
	 * 
	 * @throws IOException - SQL分読み込みエラー（トークナイザー）
	 * @throws SQLException - 構文解析エラー
	 */
	protected void parseDelete() throws IOException, SQLException{

		int token = StreamTokenizer.TT_EOF;

		while (
			(token = m_tokenizer.nextToken()) != StreamTokenizer.TT_EOF) {

			if (token == StreamTokenizer.TT_WORD) {

				int wtype = commandSet.getInt(m_tokenizer.sval);

				switch (wtype) {

					case FROM :
						//テーブルの抽出
						parseFrom();
						break;

					case WHERE :
						//WHERE句の抽出
						parseWhere();
						break;

					default :
						break;

				}
			}
			//Ver3.2 コメント対応
			else if(token == '-'){	
				forwardLineComment();
			}else if(token == '/'){
				forwardComment();
			}
		}
	}

	/**
	 * SQLコマンド判別用のマップ
	 *
	 */
	static class CommandMap extends HashMap {

		/**
		 * 
		 * @param key
		 * @return
		 * @since 1.1R4
		 */
		public int getInt(String key) {

			return getInt((Object)key.toLowerCase());

		}

		public int getInt(Object key) {

			Integer value = (Integer) get(key);

			return value == null ? -1 : value.intValue();

		}

		public int put(Object key, int value) {

			Integer oldvalue = (Integer) put(key, new Integer(value));

			return oldvalue == null ? -1 : oldvalue.intValue();

		}

	}


	/**
	 * コマンドタイプ取得
	 * @return コマンドタイプ
	 */
	public int getType() {

		return m_type;

	}

	/**
	 * テーブル名リスト取得
	 * @return 構文解析により抽出されたテーブル名の配列
	 */

	public String[] getTables() {

		String[] tables = new String[m_tables.size()];

		for (int i = 0; i < tables.length; i++) {

			TableInfo tblInfo = (TableInfo) m_tables.get(i);
			tables[i] = tblInfo.getTableName();
		}

		return tables;

	}

	/**
	 * WHERE句カラムリストの取得
	 * @param table - テーブル名
	 * @return　KEY:カラム名 VALUE:値の配列　のハッシュ（WHERE句が設定されていないときはからのマップ）
	 * 　
	 */

	public HashMap getWhereColumns(String table) {

		HashMap whereCol = null;
		try {

			TableInfo tblInf = getTableInfoByName(table);
			whereCol = tblInf.getWhereCol();
			if (whereCol == null) {
				whereCol = new HashMap();
			}

		} catch (Exception e) {
			whereCol = new HashMap();
		}

		return whereCol;

	}


	/**
	 *	UPDATEするカラム名のリストの取得
	 * @return	値の配列
	 */

	public ArrayList getSetColumns() {

		ArrayList colNames = new ArrayList();
		for (int i = 0; i < m_updateCol.size(); i++) {

			UpdateColumnInfo colInf = (UpdateColumnInfo) m_updateCol.get(i);

			colNames.add(colInf.getName());

		}

		return colNames;

	}

	/**
	 * INSERTのカラム名のリスト取得。
	 * カラム名が指定されていない場合はnull
	 * @return　INSERT対象のカラム名の配列
	 */

	public ArrayList getInsertColumns() {

		ArrayList colNames = new ArrayList();
		for (int i = 0; i < m_updateCol.size(); i++) {

			UpdateColumnInfo colInf = (UpdateColumnInfo) m_updateCol.get(i);
			String name = colInf.getName();
			if(name != null){
				colNames.add(name);
			}

		}

		if (colNames.size() == 0) {
			return null;
		}

		return colNames;

	}

	/**
	 * INSERTの値取得.
	 * カラム名（getInsertColumns()で取得した名前）を指定して、対応する値を返す
	 * 
	 * @param column カラム名
	 * @return　値
	 */

	public ParamValue getInsertValue(String column) {

		for (int i = 0; i < m_updateCol.size(); i++) {

			UpdateColumnInfo colInf = (UpdateColumnInfo) m_updateCol.get(i);
			String name = colInf.getName();
			if(name == null){
				return null;
			}
			if (column.compareToIgnoreCase(name) == 0) {
				return colInf.getValue();
			}
		}

		return null;

	}

	/**
	 * INSERTの値リスト取得.
	 * INSERTでカラム名が指定されない場合に使用される。
	 * INSERTに定義された値のリスト。
	 * 
	 * @return　値の配列（配列の順序はVALUE句に定義された順序）
	 */

	public ArrayList getInsertValues() {

		ArrayList colVals = new ArrayList();
		for (int i = 0; i < m_updateCol.size(); i++) {

			UpdateColumnInfo colInf = (UpdateColumnInfo) m_updateCol.get(i);

			colVals.add(colInf.getValue());

		}

		return colVals;

	}


	/**
	 * GROUP BY句の指定
	 * @return true -あり
	 *          false -なし
	 */
	public boolean hasGroupBy() {
		return m_groupBy;
	}

	/**
	 * ORDER BY句の指定
	 * @return true -あり
	 *          false -なし
	 */
	public boolean hasOrderBy() {
		return m_orderBy;
	}

	/**
	 * 集約関数の指定
	 * @return true -あり
	 *          false -なし
	 */
	public boolean hasFunction() {
		return m_Function;
	}

	/**
	 * テーブル結合の有無
	 * @return true -あり
	 *          false -なし
	 */
	public boolean hasJoinTable() {
		return m_JoinTable;
	}

	/**
	 * DISTINCTの有無
	 * @return true -あり
	 *          false -なし
	 * @since 1.1
	 */
	public boolean hasDistinct() {
		return m_distinct;
	}

	/**
	 * 集合演算子（UNION,INTERSECT,EXCEPT）の有無
	 * @return true -あり
	 *          false -なし
	 * @since 1.1
	 */
	public boolean hasUnion() {
		return m_union;
	}
	/**
	 * @param table
	 * @return
	 */
	protected TableInfo getTableInfo(String table) {

		TableInfo tblInf = null;

		for (int i = 0; i < m_tables.size(); i++) {
			tblInf = (TableInfo) m_tables.get(i);
			String tblName = tblInf.getTableName();
			String tblAlias = tblInf.getTableAlias();
			if (   table.equals(tblName) || (tblAlias != null && table.equals(tblAlias))) {
				break;
			}
		}

		return tblInf;
	}

	/**
	 * @param table
	 * @return
	 */
	protected TableInfo getTableInfoByName(String table) {

		TableInfo tblInf = null;

		for (int i = 0; i < m_tables.size(); i++) {
			tblInf = (TableInfo) m_tables.get(i);
			String tblName = tblInf.getTableName();
			if (table.equals(tblName)) {
				break;
			}
		}

		return tblInf;
	}
	
	
	/**
	 * FROM句の解析。
	 * テーブル名の抽出を行う。
	 * @throws IOException - SQL分読み込みエラー（トークナイザー）
	 * @throws SQLException - 構文解析エラー
	 */
	protected void parseFrom() throws IOException, SQLException {


		int token = StreamTokenizer.TT_EOF;

		while ((token = m_tokenizer.nextToken())
			!= StreamTokenizer.TT_EOF) {

			if (token == StreamTokenizer.TT_WORD	) {

				int wtype = commandSet.getInt(m_tokenizer.sval);

				switch (wtype) {

					case HAVING :
						//Ver3.1
						m_having = true;
					case UNION :
					case INTERSECT :
					case EXCEPT :
					case LIMIT :
					case OFFSET :
					case FOR :
					case GROUP :
					case ORDER :
					case WHERE :
						m_tokenizer.pushBack();
						return;


					//Ver1.1 JOINのネストに対応
					case NATURAL ://Ver2.0
					case FULL ://Ver2.0
					case INNER :
					case OUTER :
					case RIGHT :
					case LEFT :
						break;

					//@@Ver1.1 JOINのネストに対応
					case JOIN :
						m_JoinTable = true;
						parseFrom();
						break;

					case ON :
						//JOIN ** ON の条件抽出は行わない。
						parseOn();
						break;

					//Ver2.0				
					case USING :
						if( checkToken('(')){
							forwardPparenthesis();
						}else{
							throw new PSQLException(GT.tr("SQL analysis error."), ForestSQLState.INTERNAL_ERROR);
						}
						break;

					case SELECT :
						//副問い合わせが指定されています。
						//Ver 1.1 FROM句の副問い合わせは多重化テーブルと同じ扱いに
						//パーティションかどうかの判別をしないので、テーブル情報として抽出しない
						//throw new PSQLException("postgresql.forest.paser.subquery");
						//FROM句の副問い合わせは必ず括弧でくくられ、別名が定義されているので
						//これを読み飛ばす
						//括弧の読み飛ばし
						forwardPparenthesis();

						//別名の読み飛ばし
						while (
							(token = m_tokenizer.nextToken())
								!= StreamTokenizer.TT_EOF) {
												
							if (token == StreamTokenizer.TT_WORD) {
								wtype =
									commandSet.getInt(
										m_tokenizer.sval);
								if(wtype != AS){
									break;
								}
							}
							//Ver3.2 コメント対応
							else if(token == '-'){	
								forwardLineComment();
							}else if(token == '/'){
								forwardComment();
							}						
						}							

						break;

					//Ver2.0 ONLY(継承テーブルを含まないそのテーブルのみの指定)はエラーとする
					case ONLY:
						//継承テーブルに関する設定がされています。
						throw new PSQLException(GT.tr("A setup({0}) about a inheritance table is carried out.","ONLY"),ForestSQLState.INTERNAL_ERROR);
					
					default :

						TableInfo tblInfo = new TableInfo();
						tblInfo.setTableName(m_tokenizer.sval);

						//Ver1.1 FROM句にFunctionがあるときは多重化テーブルとして扱う
						//テーブル名の後ろに"("があればFunction
						//Ver1.1R4
						//if( (token = m_tokenizer.nextToken()) == '('){
						if( checkToken('(')){
							forwardPparenthesis();
							tblInfo.setTableName(null);
						}else{
							m_tokenizer.pushBack();
						}
		

						//Ver2.0 テーブル名*(継承テーブルを含むの指定)はエラーとする
						if( checkToken('*') ){
							//継承テーブルに関する設定がされています。
							throw new PSQLException(GT.tr("A setup({0}) about a inheritance table is carried out.",tblInfo.getTableName() + "*"),ForestSQLState.INTERNAL_ERROR);
						}
						m_tokenizer.pushBack();

						alias : while (
							(token = m_tokenizer.nextToken())
								!= StreamTokenizer.TT_EOF) {

							if (token == StreamTokenizer.TT_WORD) {
								wtype =
									commandSet.getInt(
										m_tokenizer.sval);

								switch (wtype) {
									case INNER :
									case OUTER :
									case RIGHT :
									case LEFT :
									case AS :

										break;

									case HAVING :
										//Ver3.1
										m_having = true;
									case UNION :
									case INTERSECT :
									case EXCEPT :
									case LIMIT :
									case OFFSET :
									case FOR :
									case GROUP :
									case ORDER :
									case WHERE :
									case ON :
										m_tokenizer.pushBack();
										break alias;

									case JOIN :
										m_JoinTable = true;
										parseFrom();
										break alias;

									default :
										//テーブルの別名セット
										tblInfo.setTableAlias(
											m_tokenizer.sval);
										break;
								}

							
							} else if (token == '(') {
								//別名の場合、列名の別名を指定されるので、これを読み飛ばす
								forwardPparenthesis();
							} else if (token == ',') {
								break;
							}
							//Ver3.2 コメント対応
							else if(token == '-'){	
								forwardLineComment();
							}else if(token == '/'){
								forwardComment();
							}

						}
						
						//Ver1.1 関数が指定されたときはテーブル名がnullになるので、
						//テーブル情報をセットしない
						if(tblInfo.getTableName() == null){
							break;
						}

						m_tables.add(tblInfo);
						

						break;

				}

			}
			//Ver3.2 コメント対応
			else if(token == '-'){	
				forwardLineComment();
			}else if(token == '/'){
				forwardComment();
			}

		}
	}

	/**
	 * WHERE句 IN() 構文解析 
	 * @throws IOException - SQL分読み込みエラー（トークナイザー）
	 * @throws SQLException - 構文解析エラー
	 */
	protected ArrayList parseWhereIn() throws IOException, SQLException {


		int token = StreamTokenizer.TT_EOF;

		ArrayList values = new ArrayList();

		ParamValue value = new ParamValue();

		boolean isQuoteChar = false;//文字列判別
		boolean isExit = false;//

		while (
			(token = m_tokenizer.nextToken())
				!= StreamTokenizer.TT_EOF) {


			switch (token) {

				case '?' :
					if(!isQuoteChar){
						value = new ParamValue(m_tokenizer.getParamNum());
						
						m_paramList.add(value);
						
					}else{
						value.append((char)m_tokenizer.ttype); 
					}
					break ;
	
				
				case ')' :
					if(isQuoteChar){
						value.append((char)m_tokenizer.ttype); 
					}else{
						isExit = true;
					}
				case ',' :
					if(isQuoteChar){
						value.append((char)m_tokenizer.ttype); 
					}else{
						values.add(value);
						value = new ParamValue();
					}
					break ;

				//Ver3.2 コメント対応	
				case '-' :
					if(isQuoteChar){
						value.append((char)m_tokenizer.ttype); 
					}else if(forwardLineComment() != true){
						//コメントじゃなければ
						values.clear();
						//対応する括弧まで進める
						forwardPparenthesis();
						isExit = true;
					}
					break ;

				//Ver3.2 コメント対応	
				case '/' :
					if(isQuoteChar){
						value.append((char)m_tokenizer.ttype); 
					}else if(forwardComment() != true){
						//コメントじゃなければ
						values.clear();
						//対応する括弧まで進める
						forwardPparenthesis();
						isExit = true;
					}
					break ;

				case '+' :
				case '*' :
				case '%' :
					//演算子
					if(isQuoteChar){
						value.append((char)m_tokenizer.ttype); 
					}else{
						values.clear();
						//対応する括弧まで進める
						forwardPparenthesis();
						isExit = true;
					}
					break ;

				//Ver3.2 \'の処理修正
				case '\\' :
					if(isQuoteChar ){
						value.append((char) m_tokenizer.ttype);

						//\\か\'のばあい、1文字として取り込む
						token = m_tokenizer.nextToken();
						if(token == '\\' || token == '\''){
							value.append((char)m_tokenizer.ttype);
						}else{
							m_tokenizer.pushBack();
						}
						
					}
					break;

				case '\'':

					if(isQuoteChar ){
						//Ver3.2 \'の処理修正
//						//\'だったらそのまま
//						int length = value.length();
//						if(length > 0 &&
//						   value.substring(length-1).equals("\\") ){
//							value.append((char) m_tokenizer.ttype);
//							break;
//						}
							
						//''だったら\'に変換
						token = m_tokenizer.nextToken();
						if(token == '\''){
							value.append("\\'");
							break;
						}
						//それ以外はトークナイザーのカレントを戻してフラグをoff（文字列のくくりを終わる）
						m_tokenizer.pushBack();
					}
					isQuoteChar = !isQuoteChar;//文字列判断フラグをトグル切り替え
					break;
					
				case StreamTokenizer.TT_WORD :

					if(isQuoteChar){
						value.append(m_tokenizer.sval);
					}else if(isNumeric(m_tokenizer.sval)){
						value.append(m_tokenizer.sval);
					}else{
						int wtype =
							commandSet.getInt(m_tokenizer.sval);
						if (wtype == SELECT) {
							//幅問い合わせ
							values.clear();
							//対応する括弧まで進める
							forwardPparenthesis();
							isExit = true;
						}
					}

					break;


				default :
					//Ver1.1R4
					if(isQuoteChar){
						value.append((char)m_tokenizer.ttype); 
					}

					break;
			}

			if(isExit){
				break;
			}

		}


		return values;



	}
	/**
	 * WHERE句構文解析 
	 * @throws IOException - SQL分読み込みエラー（トークナイザー）
	 * @throws SQLException - 構文解析エラー
	 */
	protected void parseWhere() throws IOException, SQLException {

		int token = StreamTokenizer.TT_EOF;

		HashMap colMap = new HashMap();

		//Ver1.1R4
		//String value = null;
		ParamValue value = new ParamValue();
		//パラメータ
		
		//Ver1.1R4 文字列('でくくられているかどうか）の判断
		boolean isQuoteChar = false;

		boolean stop  = false; //解析中止用（中止でもWHERE句の終わりまで進むため）

		String key = null;

		where : while (
			(token = m_tokenizer.nextToken()) != StreamTokenizer.TT_EOF) {

			switch (token) {

				case '?' :
					if(!isQuoteChar){
						value = new ParamValue(m_tokenizer.getParamNum());
						m_paramList.add(value);
						
					}
					value.append((char)m_tokenizer.ttype); 
					break ;

				//Ver3.2 コメント対応	
				case '-' :
					if(isQuoteChar){
						value.append((char)m_tokenizer.ttype); 
					}else if(forwardLineComment() != true){
						//コメントじゃなければ
						key = null;
					}
					break ;
	
					//Ver3.2 コメント対応	
				case '/' :
					if(isQuoteChar){
						value.append((char)m_tokenizer.ttype); 
					}else if(forwardComment() != true){
						//コメントじゃなければ
						key = null;
					}
					break ;

//				Ver1.1R4
				case '+' :
				case '*' :
				case '%' :
					//演算子
					if(isQuoteChar){
						value.append((char)m_tokenizer.ttype); 
					}else{
						key = null;
					}
					break ;

				case '=' :

					break;

				case '<' :
				case '>' :
				case '!' :

					key = null;

					break;


				//Ver3.2 \'の処理修正
				case '\\' :
					if(isQuoteChar ){
						value.append((char) m_tokenizer.ttype);
					
						//\\か\'のばあい、1文字として取り込む
						token = m_tokenizer.nextToken();
						if(token == '\\' || token == '\''){
							value.append((char)m_tokenizer.ttype);
						}else{
							m_tokenizer.pushBack();
						}
							
					}
					break;
					
					
				case '\'':

					if(isQuoteChar ){
				//Ver3.2 \'の処理修正
//						//\'だったらそのまま
//						int length = value.length();
//						if(length > 0 &&
//						   value.substring(length-1).equals("\\") ){
//							value.append((char) m_tokenizer.ttype);
//							break;
//						}
							
						//''だったら\'に変換
						token = m_tokenizer.nextToken();
						if(token == '\''){
							value.append("\\'");
							break;
						}
						//それ以外はトークナイザーのカレントを戻してフラグをoff（文字列のくくりを終わる）
						m_tokenizer.pushBack();
					}
					isQuoteChar = !isQuoteChar;//文字列判断フラグをトグル切り替え
					break;


				case StreamTokenizer.TT_WORD :

					int wtype = commandSet.getInt(m_tokenizer.sval);
					switch (wtype) {

						case INNER :
						case OUTER :
						case RIGHT :
						case LEFT :
							//ONの解析時
							break where;

						case HAVING :
							//Ver3.1
							m_having = true;

						case WHERE :
							//ONの解析時

						case GROUP :
						case UNION :
						case INTERSECT :
						case EXCEPT :
						case ORDER :
						case LIMIT :
						case OFFSET :
						case FOR :
							m_tokenizer.pushBack();
							break where;

						case SELECT :
							//副問い合わせが指定されています。
							//throw new PSQLException("postgresql.forest.paser.subquery");
							//m_tables.clear();
							//return;
							//対応する括弧まで進める
							forwardPparenthesis();
							break;
							
							
						case AND :
							//ここでデータセット

							//MOD Ver1.1 式＝式の場合に、右辺をkeyとみなしてvalueがnullとなってしまい、右辺がパーティション属性の場合エラーとなっていた
							//if (key != null) {
							//Ver1.1R4
							//if (key != null && value != null) {
							if (key != null && value.length() != 0) {

								ArrayList values = null;

								if (colMap.containsKey(key)) {
									values = (ArrayList) colMap.get(key);
								} else {
									values = new ArrayList();
								}
								//Ver1.1R4
								//values.add(value);
								values.add(value);
								colMap.put(key, values);

							}
							key = null;
							//Ver1.1R4
							//value = null;
							value = new ParamValue();

							break;

						case OR :
						case LIKE :
						case NOT :
						case CAST :
							//'NOT' 'OR'が含まれている場合、解析中止
							//CAST が含まれている場合、解析中止
							stop = true;
							break;


						case IN :
							//ここでデータセット

							ArrayList values = parseWhereIn();

							if (!values.isEmpty()) {
								colMap.put(key, values);
							}

							key = null;
							//Ver1.1R4
							//value = null;
							value = new ParamValue();

							break;

						default :
							//Ver1.1R4
							//key = m_tokenizer.sval;
							if(isQuoteChar){
								value.append(m_tokenizer.sval); 
							}else if(isNumeric(m_tokenizer.sval)){
								value.append(m_tokenizer.sval); 
							}else{
								//20050516 BUGFIX
								//key = m_tokenizer.sval;
								key = m_tokenizer.sval.toLowerCase();
							}

							break;

					}

					break;

				default :

					//Ver1.1R4
					if(isQuoteChar){
						value.append((char)m_tokenizer.ttype); 
					}
					break;

			}

		}

		if(stop)
			return;
			
		
		//MOD Ver1.1 式＝式の場合に、右辺をkeyとみなしてvalueがnullとなってしまい、右辺がパーティション属性の場合エラーとなっていた
		//if (key != null) {
		//Ver1.1R4
		//if (key != null && value != null) {
		if (key != null && value.length() != 0) {

			ArrayList values = null;

			if (colMap.containsKey(key)) {
				values = (ArrayList) colMap.get(key);
			} else {
				values = new ArrayList();
			}
			//Ver1.1R4
			//values.add(value);
			values.add(value);
			colMap.put(key, values);

			
		}

		//Where句の解析結果をテーブルに紐付ける。
		Iterator iter = colMap.keySet().iterator();
		while (iter.hasNext()) {
			String strkey = (String) iter.next();

			HashMap colInfo = null;
			
			//Ver3.0 無駄なsplitの実行をなくす
			if ( stringContains(strkey, '.') ){
			    String[] keyspl = strkey.split("[.]");
			    
				//該当するテーブルにセット
				TableInfo tblInf = getTableInfo(keyspl[0]);
				//Ver2.0 FROM句を指定しないSQLに対応
				if(tblInf != null ){
					tblInf.addWhereCol(keyspl[1], (ArrayList) colMap.get(strkey));
				}				
			} else {
				//すべてのテーブル情報にセット\
				for (int i = 0; i < m_tables.size(); i++) {
					TableInfo tblInf = (TableInfo) m_tables.get(i);
					tblInf.addWhereCol(strkey, (ArrayList) colMap.get(strkey));

				}
			}
		}
	}
	
	/**
	 * 文字列内に、指定文字が存在するかチェックする
	 * @since 3.0
	 */
	private boolean stringContains(String str, char ch)
	{
	    for (int i=0 ; i<str.length() ; i++)
	        if ( str.charAt(i)==ch )
	            return true;
	        
	    return false;
	}
	
	/**
	 * 次の括弧まで進める
	 * @throws IOException
	 * @since 1.1
	 */
	protected void forwardPparenthesis() throws IOException {
		int token;
		int nest = 1;
		while (
			(token = m_tokenizer.nextToken())
				!= StreamTokenizer.TT_EOF) {
							
			if (token == '('){
				nest++;				
			}else if (token == ')'){
				nest--;
				if(nest == 0){
					break;
				}
		
			}								
			//Ver3.2 コメント対応
			else if(token == '-'){	
				forwardLineComment();
			}else if(token == '/'){
				forwardComment();
			}							
		}
	}

	/**
	 * コメント行読み飛ばし
	 * 行コメント（--から行末まで）を読み飛ばし
	 * @return	true:コメントを読み飛ばした。
	 * 			false：コメントではないのでそのまま
	 * @throws IOException
	 * @since 3.2
	 */
	protected boolean forwardLineComment() throws IOException {
		int token;

		//次の文字がハイフンなら行コメント
		token = m_tokenizer.nextToken();
		if(token != '-'){
			m_tokenizer.pushBack();
			return false;
		}

		while (
			(token = m_tokenizer.nextToken())
				!= StreamTokenizer.TT_EOF) {
							
			//改行コードが来るまで読み飛ばし
			if(token == StreamTokenizer.TT_EOL){
				break;
			}
		}
		return true;
		
	}

	/**
	 * コメント読み飛ばし
	 * C言語スタイル（ネスト有り）を読み飛ばす
	 * @return	true:コメントを読み飛ばした。
	 * 			false：コメントではないのでそのまま
	 * @throws IOException
	 * @since 3.2
	 */
	protected boolean forwardComment() throws IOException {
		int token;
		int nest = 1;

		//次の文字が、*ならばコメントブロック
		token = m_tokenizer.nextToken();
		if(token == '*'){
			nest = 1;
		}else{
			m_tokenizer.pushBack();
			return false;
		}

		
		
		while (
			(token = m_tokenizer.nextToken())
				!= StreamTokenizer.TT_EOF) {

			
			
			if(  token == '*' ){
				
				//次の文字が、/ならばコメントネスト減算
				token = m_tokenizer.nextToken();
				if(token == '/'){
					nest--;
					if(nest == 0){
						//コメントブロック終わり
						break;
					}
				}else{
					m_tokenizer.pushBack();
				}
				
			}else if(token == '/'){

				//次の文字が、*ならばコメントネスト加算
				token = m_tokenizer.nextToken();
				if(token == '*'){
					nest++;
				}else{
					m_tokenizer.pushBack();
				}
				
			}
			
		}					
		return true;

		
	}
	
	/**
	 * JOIN * ON句構文解析
	 * パーティション属性の抽出条件とはしないので、
	 * ON句が終わるまで読み飛ばす 
	 *
	 * @throws IOException - SQL分読み込みエラー（トークナイザー）
	 */	
	protected void parseOn() throws IOException {
	
		int token = StreamTokenizer.TT_EOF;
	
		while (
			(token = m_tokenizer.nextToken()) != StreamTokenizer.TT_EOF) {
					
			if( token ==  StreamTokenizer.TT_WORD){
	
				int wtype = commandSet.getInt(m_tokenizer.sval);
				switch (wtype) {

					case INNER :
					case OUTER :
					case RIGHT :
					case LEFT :
						return;

					case HAVING :
						//Ver3.1
						m_having = true;
					case WHERE :
					case GROUP :
					case UNION :
					case INTERSECT :
					case EXCEPT :
					case ORDER :
					case LIMIT :
					case OFFSET :
					case FOR :
						m_tokenizer.pushBack();
						return;
	
	
				default :
					break;
	
				}
			}
			//Ver3.2 コメント対応
			else if(token == '-'){	
				forwardLineComment();
			}else if(token == '/'){
				forwardComment();
			}
	
		}
	
			
	
	}
	/**
	 * GROUP BY句構文解析
	 * グルーピング情報を作成する
	 *
	 * @throws IOException - SQL分読み込みエラー（トークナイザー）
	 * @since 2.0
	 */	
	protected void parseGroupBy() throws IOException {
	
		int token = StreamTokenizer.TT_EOF;
		
		boolean execFlg = true;
		
		ColumnInfo columnInfo = null;
	
		while (
			(token = m_tokenizer.nextToken()) != StreamTokenizer.TT_EOF) {
					
			if( token ==  StreamTokenizer.TT_WORD){

				//Ver1.1R4
				
				if(isNumeric(m_tokenizer.sval)){
					if( !execFlg){
						continue;
					}		

					int number = new Double(m_tokenizer.sval).intValue(); 
			
					columnInfo = new OrderInfo(number);
					m_groupList.add(columnInfo);
				
				}else{
					
					int wtype = commandSet.getInt(m_tokenizer.sval);
					switch (wtype) {
	
						//Ver1.1R4
						case BY :
							//BY の読み飛ばし
							break;

						case UNION :
						case INTERSECT :
						case EXCEPT :
						case ORDER :
						case LIMIT :
						case OFFSET :
						case FOR :
							m_tokenizer.pushBack();
							return;
						
						//Ver3.0 パーティション化2でエラーとする為
						case HAVING :
						    m_having = true;
							m_tokenizer.pushBack();
							return;

						default :
							if( !execFlg){
								continue;
							}		
	
							columnInfo = new OrderInfo(m_tokenizer.sval);
							m_groupList.add(columnInfo);
							
							//SELECT列から、序列を求める
							for (int i = 0; i < m_selectList.size(); i++){

							    SelectColumnInfo selectColumnInfo = (SelectColumnInfo) m_selectList.get(i);
									
								if(selectColumnInfo.getAlias() != null ){
									//Alias で序列を特定 
									if(m_tokenizer.sval.compareToIgnoreCase(selectColumnInfo.getAlias()) == 0  ){
									 	columnInfo.setNumber(selectColumnInfo.getNumber());
										break;					
									}
											   
	
							    }else if(selectColumnInfo.getName() != null && 
								   m_tokenizer.sval.compareToIgnoreCase(selectColumnInfo.getName()) == 0  ){
									//列名 で序列を特定
	
									columnInfo.setNumber(selectColumnInfo.getNumber());
									break;							   
							   }
		
											
							}
							if(columnInfo.getNumber() == 0){
								//SELECT列に存在しなかった。
								//序列が特定できないカラムがある
								m_groupList.clear();
								execFlg = false;
							}
							break;
		
					}
				}
			} 	
			//Ver3.2 コメント対応
			else if(token == '-'){	
				forwardLineComment();
			}else if(token == '/'){
				forwardComment();
			}
		}
		
	
	}

	/**
	 * ORDER BY句構文解析
	 * ソート情報を作成する
	 *
	 * @throws IOException - SQL分読み込みエラー（トークナイザー）
	 * @since 2.0
	 */	
	protected void parseOrderBy() throws IOException {
	
		int token = StreamTokenizer.TT_EOF;

		boolean execFlg = true;
		
		OrderInfo orderInfo = null;
	
		while (
			(token = m_tokenizer.nextToken()) != StreamTokenizer.TT_EOF) {
					
			if( token ==  StreamTokenizer.TT_WORD){
				//Ver1.1R4
				
				if(isNumeric(m_tokenizer.sval)){
					if( !execFlg){
						continue;
					}		


					int number = new Double(m_tokenizer.sval).intValue(); 
			
					orderInfo = new OrderInfo(number);
					m_orderList.add(orderInfo);

				
				}else{
	
					int wtype = commandSet.getInt(m_tokenizer.sval);
					switch (wtype) {
	
						//Ver1.1R4
						case BY :
							//BY の読み飛ばし
							break;
							
						case LIMIT :
						case OFFSET :
						case FOR :
							m_tokenizer.pushBack();
							return;
		
						case DESC :
							orderInfo.setSort(OrderInfo.DESC);
							break;
		
						case USING :
							//USINGが使用されている場合はVEIWでの処理となるので、
							//ソート情報をクリア
							m_orderList.clear();
							execFlg = false;
							break;
	
						default :
							if( !execFlg){
								continue;
							}		
	
							orderInfo = new OrderInfo(m_tokenizer.sval);
							m_orderList.add(orderInfo);
							
							//SELECT列から、序列を求める
							for (int i = 0; i < m_selectList.size(); i++){

							    SelectColumnInfo selectColumnInfo = (SelectColumnInfo) m_selectList.get(i);
										
								//Alias で序列を特定
								if(selectColumnInfo.getAlias() != null ){
									if(m_tokenizer.sval.compareToIgnoreCase(selectColumnInfo.getAlias()) == 0  ){
										orderInfo.setNumber(selectColumnInfo.getNumber());
										break;
									}							   
								}else if(  selectColumnInfo.getName() != null ){ 
									//列名 で序列を特定
	
									//設定した列より前にアスタリスクが設定されていると
									//結果列が予測できない。結果の序列でソートするのでこの場合はVIEW
									if(selectColumnInfo.getName().indexOf('*') != -1 ){
										break;							   
									}
									   
									if( m_tokenizer.sval.compareToIgnoreCase(selectColumnInfo.getName()) == 0  ){
										orderInfo.setNumber(selectColumnInfo.getNumber());
										break;							   
									}
								}
							}
	
							if(orderInfo.getNumber() == 0){
								
								//SELECT列に存在しなかった。
								//序列が特定できないカラムがある
								m_orderList.clear();
								execFlg = false;
							}
							break;
		
					}
				}
			}
			//Ver3.2 コメント対応
			else if(token == '-'){	
				forwardLineComment();
			}else if(token == '/'){
				forwardComment();
			}
		}
		
			
	
	}
	/**
	 * SET句文構文解析 
	 *
	 * @throws IOException - SQL分読み込みエラー（トークナイザー）
	 */
	
	protected void parseSet() throws IOException {
	
		int token = StreamTokenizer.TT_EOF;
	
		//Ver1.1R4
		//String value = null;
		ParamValue value = new ParamValue();
		String key = null;
	

		//Ver1.1R4 文字列('でくくられているかどうか）の判断
		boolean isQuoteChar = false;

		while ((token = m_tokenizer.nextToken()) != StreamTokenizer.TT_EOF) {
	
			switch (token) {
	
				case ',' :
					//Ver1.1R4
					if(isQuoteChar){
						value.append((char)m_tokenizer.ttype); 
						break;
					}
	
					//ここでデータセット
					if (key != null) {
						m_updateCol.add(new UpdateColumnInfo(key, value)) ;
					}
					key = null;
					
					//Ver1.1R4
					//value = null;
					value = new ParamValue();
	
					break;


				case '=' :
	
					break;
	
				//Ver3.2 \'の処理修正
				case '\\' :
					if(isQuoteChar ){
						value.append((char) m_tokenizer.ttype);
					
						//\\か\'のばあい、1文字として取り込む
						token = m_tokenizer.nextToken();
						if(token == '\\' || token == '\''){
							value.append((char)m_tokenizer.ttype);
						}else{
							m_tokenizer.pushBack();
						}
							
					}
				break;

				case '\'':

					if(isQuoteChar ){
						//Ver3.2 \'の処理修正
//						//\'だったらそのまま
//						int length = value.length();
//						if(length > 0 &&
//						   value.substring(length-1).equals("\\") ){
//							value.append((char) m_tokenizer.ttype);
//							break;
//						}
							
						//''だったら\'に変換
						token = m_tokenizer.nextToken();
						if(token == '\''){
							value.append("\\'");
							break;
						}
						//それ以外はトークナイザーのカレントを戻してフラグをoff（文字列のくくりを終わる）
						m_tokenizer.pushBack();
					}
					isQuoteChar = !isQuoteChar;//文字列判断フラグをトグル切り替え
					break;
	
	
				case StreamTokenizer.TT_WORD :

	
					if(isQuoteChar){
						value.append(m_tokenizer.sval);
					}else if(isNumeric(m_tokenizer.sval)){
						value.append(m_tokenizer.sval);
					}else{

						int wtype = commandSet.getInt(m_tokenizer.sval);
						switch (wtype) {
		
							case FROM :
							case WHERE :
								if (key != null) {
									m_updateCol.add(new UpdateColumnInfo(key, value)) ;
								}
								m_tokenizer.pushBack();
								return;
		
							case SELECT :
								//副問い合わせ
								//括弧の先まで進める
								forwardPparenthesis();
								//value = null;//このカラムはパーティション特定できないように設定
								value = new ParamValue();
								
								break ;
							
							default :
								key = m_tokenizer.sval;
								break;
		
						}
					}
	
					break;

				//Ver3.2 コメント対応	
				case '-' :
					if(isQuoteChar){
						value.append((char)m_tokenizer.ttype); 
					}else{
						forwardLineComment();
					}
					break ;
	
				//Ver3.2 コメント対応	
				case '/' :
					if(isQuoteChar){
						value.append((char)m_tokenizer.ttype); 
					}else {
						forwardComment();
					}
					break ;
					
				default :
					//Ver1.1R4
					if(isQuoteChar){
						value.append((char)m_tokenizer.ttype); 
					}
	
					break;
	
			}
	
		}
	
			
		if (key != null) {
			m_updateCol.add(new UpdateColumnInfo(key, value)) ;
		}
	
	
	
	}

	/**
	 * VALUES句文構文解析 
	 *
	 * @throws IOException - SQL分読み込みエラー（トークナイザー）
	 */
		
	protected void parseValues() throws IOException {
		
		int token = StreamTokenizer.TT_EOF;
		
		int colIdx = 0;

		//Ver1.1R4
		//String value = null;
		ParamValue value = new ParamValue();
		
		//Ver1.1R4 文字列('でくくられているかどうか）の判断
		boolean isQuoteChar = false;
		
		
		while ( (token = m_tokenizer.nextToken()) != StreamTokenizer.TT_EOF) {
		
			switch (token) {

				case '?' :
					if(!isQuoteChar){
						value = new ParamValue(m_tokenizer.getParamNum());
						m_paramList.add(value);
						
					}
					value.append((char)m_tokenizer.ttype); 
					break ;

			
				case ',' :
					//Ver1.1R4
					if(isQuoteChar){
						value.append((char)m_tokenizer.ttype); 
						break;
					}

					//ここでデータセット
					if(m_updateCol.size() > colIdx) {
						UpdateColumnInfo upColInf = (UpdateColumnInfo) m_updateCol.get(colIdx);
						upColInf.setValue(value);
					} else {
						UpdateColumnInfo upColInf = new UpdateColumnInfo(value);
						m_updateCol.add(upColInf);
					}

					//Ver1.1R4
					//value = null;
					value = new ParamValue();

					colIdx++;
					break;
	
					//Ver3.2 コメント対応	
				case '-' :
					if(isQuoteChar){
						value.append((char)m_tokenizer.ttype); 
					}else if(forwardLineComment() != true){
						//コメントじゃなければ

						//（空で）データセット
						//Ver1.1R4
						value = new ParamValue();
						if(m_updateCol.size() > colIdx) {
							UpdateColumnInfo upColInf = (UpdateColumnInfo) m_updateCol.get(colIdx);
							upColInf.setValue(null);
						} else {
							UpdateColumnInfo upColInf = new UpdateColumnInfo(value);
							m_updateCol.add(upColInf);
						}


						//次のカラムまで進める
						colIdx++;
						while ( (token = m_tokenizer.nextToken()) != StreamTokenizer.TT_EOF) {
							if(token == ','){
								break;
							}
							//Ver3.2 コメント対応
							else if(token == '-'){	
								forwardLineComment();
							}else if(token == '/'){
								forwardComment();
							}
						}
					}
	
				//Ver3.2 コメント対応	
				case '/' :
					if(isQuoteChar){
						value.append((char)m_tokenizer.ttype); 
					}else if(forwardComment() != true){
						//コメントじゃなければ
						//（空で）データセット
						//Ver1.1R4
						value = new ParamValue();
						if(m_updateCol.size() > colIdx) {
							UpdateColumnInfo upColInf = (UpdateColumnInfo) m_updateCol.get(colIdx);
							upColInf.setValue(null);
						} else {
							UpdateColumnInfo upColInf = new UpdateColumnInfo(value);
							m_updateCol.add(upColInf);
						}


						//次のカラムまで進める
						colIdx++;
						while ( (token = m_tokenizer.nextToken()) != StreamTokenizer.TT_EOF) {
							if(token == ','){
								break;
							}
							//Ver3.2 コメント対応
							else if(token == '-'){	
								forwardLineComment();
							}else if(token == '/'){
								forwardComment();
							}
						}
					}
					break ;
	
				case '*' :
				case '+' :
				case '%' :
					//Ver1.1R4
					if(isQuoteChar){
						value.append((char)m_tokenizer.ttype); 
						break;
					}
					//（空で）データセット
					//Ver1.1R4
					value = new ParamValue();
					if(m_updateCol.size() > colIdx) {
						UpdateColumnInfo upColInf = (UpdateColumnInfo) m_updateCol.get(colIdx);
						upColInf.setValue(null);
					} else {
						UpdateColumnInfo upColInf = new UpdateColumnInfo(value);
						m_updateCol.add(upColInf);
					}


					//次のカラムまで進める
					colIdx++;
					while ( (token = m_tokenizer.nextToken()) != StreamTokenizer.TT_EOF) {
						if(token == ','){
							break;
						}
						//Ver3.2 コメント対応
						else if(token == '-'){	
							forwardLineComment();
						}else if(token == '/'){
							forwardComment();
						}
					}
					break;
		
				case StreamTokenizer.TT_WORD :
		
					if(isQuoteChar){
						value.append(m_tokenizer.sval);
					}else if(isNumeric(m_tokenizer.sval)){
						value.append(m_tokenizer.sval);
					}else{

						int wtype = commandSet.getInt(m_tokenizer.sval);
						if(wtype == SELECT ){
							//副問い合わせ
							
							//次の項目まで進める
							colIdx++;
							
							while ( (token = m_tokenizer.nextToken()) != StreamTokenizer.TT_EOF) {
								if(token == ','){
									break;
								}
								//Ver3.2 コメント対応
								else if(token == '-'){	
									forwardLineComment();
								}else if(token == '/'){
									forwardComment();
								}
							}
						}

						//データセット
						value = new ParamValue();
						if(m_updateCol.size() > colIdx) {
							UpdateColumnInfo upColInf = (UpdateColumnInfo) m_updateCol.get(colIdx);
							upColInf.setValue(null);
						} else {
							UpdateColumnInfo upColInf = new UpdateColumnInfo( value);
							m_updateCol.add(upColInf);
						}
	
					}
		
					break;
		
				//Ver3.2 \'の処理修正
				case '\\' :
					if(isQuoteChar ){
							value.append((char) m_tokenizer.ttype);
					
						//\\か\'のばあい、1文字として取り込む
						token = m_tokenizer.nextToken();
						if(token == '\\' || token == '\''){
							value.append((char)m_tokenizer.ttype);
						}else{
							m_tokenizer.pushBack();
						}
					}
					break;
		
				case '\'':
					if(isQuoteChar ){
						//Ver3.2 \'の処理修正
//						//\'だったらそのまま
//						int length = value.length();
//						if(length > 0 &&
//						   value.substring(length-1).equals("\\") ){
//							value.append((char) m_tokenizer.ttype);
//							break;
//						}
						//''だったら\'に変換
						token = m_tokenizer.nextToken();
						if(token == '\''){
							value.append("\\'");
							break;
						}
						//それ以外はトークナイザーのカレントを戻してフラグをoff（文字列のくくりを終わる）
						m_tokenizer.pushBack();
					}
					isQuoteChar = !isQuoteChar;//文字列判断フラグをトグル切り替え
					break;
		
				default :
		
					//Ver1.1R4
					if(isQuoteChar){
						value.append((char)m_tokenizer.ttype); 
					}
		
			}
		
		}
		
				
		//Ver1.1R4
		//if(value != null ){
		if(value.length() != 0 ){
			try {
				UpdateColumnInfo upColInf = (UpdateColumnInfo) m_updateCol.get(colIdx);
				upColInf.setValue(value);
			} catch (RuntimeException e) {
				UpdateColumnInfo upColInf = new UpdateColumnInfo( value);
				m_updateCol.add(upColInf);
			}
		}

		
	}

	/**
	 * INSERT INTO ( column ) 文構文解析 
	 *
	 * @throws IOException - SQL分読み込みエラー（トークナイザー）
	 * @throws SQLException - 構文解析エラー
	 */
	protected void parseColumns() throws IOException, SQLException {
		
	    //Ver3.0 INSERT INTO ・・・ (SELECT ・・・) をエラーとする。
	    if(checkToken(StreamTokenizer.TT_WORD)){
			int wtype = commandSet.getInt(m_tokenizer.sval);
			if(wtype == SELECT){
				//INSERT SELECTが指定されています。
				throw new PSQLException(GT.tr("INSERT SELECT is specified."), ForestSQLState.INTERNAL_ERROR);
			}
		}
		m_tokenizer.pushBack();
	    
		int token = StreamTokenizer.TT_EOF;

		//Ver2.0
		StringBuffer value = new StringBuffer();

		while (
			(token = m_tokenizer.nextToken()) != StreamTokenizer.TT_EOF) {
			
			if(token == StreamTokenizer.TT_WORD){
				if(value.length() == 0){
					value.append(m_tokenizer.sval); 
				}

			//Ver2.0 -->				
			}else if(token == ','){
				m_updateCol.add(new UpdateColumnInfo(value.toString()) );
				value.delete(0, value.length());
			//Ver2.0 <--				
			}else if(token == ')'){
				//Ver2.0
				m_updateCol.add(new UpdateColumnInfo(value.toString()));
				break;
			}
			//Ver3.2 コメント対応
			else if(token == '-'){	
				forwardLineComment();
			}else if(token == '/'){
				forwardComment();
			}
		
		}
			
			
			
	}
	/**
	 *
	 */
	protected class TableInfo {

		protected String m_tableName = null; //テーブル名
		protected String m_tableAlias = null; //テーブルの別名

		protected HashMap m_whereCol = null; //Whereカラム

		/**
		 * @return
		 */
		public String getTableAlias() {
			return m_tableAlias;
		}

		/**
		 * @return
		 */
		public String getTableName() {
			return m_tableName;
		}

		/**
		 * @return
		 */
		public ArrayList getUpdateCol() {
			return m_updateCol;
		}

		/**
		 * @return
		 */
		public HashMap getWhereCol() {
			return m_whereCol;
		}

		/**
		 * @param string
		 */
		public void setTableAlias(String string) {
			//Ver1.1R4
			//m_tableAlias = string;
			m_tableAlias = string.toLowerCase();
		}

		/**
		 * @param string
		 */
		public void setTableName(String string) {
			//Ver1.1R4
			//m_tableName = string;

			//Ver3.0 BUGFIX
			//Modified by ktany
			//m_tableName = string.toLowerCase();
			//FROM句に関数が来たときは読み飛ばすためにnullがセットされる
			if(string == null){
				m_tableName = null;
			}
			else{
				m_tableName = string.toLowerCase();
			}

		}

		/**
		 * @param list
		 */
		public void setUpdateCol(ArrayList list) {
			m_updateCol = list;
		}

		/**
		 * @param map
		 */
		public void setWhereCol(HashMap map) {
			m_whereCol = map;
		}

		/**
		 * @param colName
		 * @param values
		 */
		public void addWhereCol(String colName, ArrayList newVals) {

			if (m_whereCol == null) {
				m_whereCol = new HashMap();
			}
			if (m_whereCol.containsKey(colName)) {
				ArrayList vals = (ArrayList) m_whereCol.get(colName);
				vals.addAll(newVals);
			} else {
				m_whereCol.put(colName, newVals);
			}

		}

	}

	/**
	 *
	 */
	protected class UpdateColumnInfo {
		protected String m_name; //カラム名
		protected ParamValue m_value; //値


		/**
		 * 
		 * @param value
		 */
		public UpdateColumnInfo( ParamValue value) {
			this(null,value);


		}
		
		
		/**
		 * 
		 * @param name
		 * @param value
		 * @since 1.1R4
		 */
		public UpdateColumnInfo(String name) {
			m_name = name;
			m_value = null;


		}

		/**
		 * 
		 * @param name
		 * @param value
		 */
		public UpdateColumnInfo(String name, ParamValue value) {
			m_name = name;

			if(value == null || value.length() == 0){
				m_value = null;
			}else{
				m_value = value;
			}

		}


		/**
		 * @return
		 */
		public String getName() {
			return m_name;
		}

		/**
		 * @return
		 */
		public ParamValue getValue() {
			return m_value;
		}

		/**
		 * @param string
		 */
		public void setName(String string) {
			m_name = string;
		}

		/**
		 * @param value
		 */
		public void setValue(ParamValue value) {
			m_value = value;
		}


	}


	/**
	 * FOR UPDATE が指定されているか
	 * @return
	 * @since 1.1
	 */
	public boolean isForUpdate() {
		return m_ForUpdate;
	}

	/**
	 * @return
	 * @since 2.0
	 */
	public ArrayList getOrderList() {
		return m_orderList;
	}
	/**
	 * @return
	 * @since 2.0
	 */
	public ArrayList getGroupList() {
		return m_groupList;
	}

	/**
	 * @return
	 * @since 2.0
	 */
	public boolean hasLimit() {
		return m_limit;
	}

	/**
	 * @return
	 * @since 2.0
	 */
	public int getLimitCount() {
		return m_limitCount;
	}

	/**
	 * @return
	 * @since 2.0
	 */
	public boolean hasOffset() {
		return m_offset;
	}


	/**
	 * @return
	 * @since 2.0
	 */
	public ArrayList getSelectList() {
		return m_selectList;
	}

	/**
	 * @return
	 * @since 2.0
	 */
	public boolean hasExecFunction() {
		return m_execFunction;
	}

	/**
	 * 数値チェック.
	 * 文字列が数値化チェックする
	 * @@とりあえずの暫定（NUMERICとか対応できない？）
	 * @param val
	 * @return
	 * @since1.1R4
	 */
	protected boolean isNumeric(Object val){
	    String s = (String)val;
	    
	    //Ver3.0 無駄な NumberFormatException の発生を防止
	    if ( s.charAt(0)!='0' &&
	            s.charAt(0)!='1' &&
	            s.charAt(0)!='2' &&
	            s.charAt(0)!='3' &&
	            s.charAt(0)!='4' &&
	            s.charAt(0)!='5' &&
	            s.charAt(0)!='6' &&
	            s.charAt(0)!='7' &&
	            s.charAt(0)!='8' &&
	            s.charAt(0)!='9' &&
	            s.charAt(0)!='.' &&
	            s.charAt(0)!='+' &&
	            s.charAt(0)!='-' )
	        return false;
	    
		try {
			Double name = new Double(val.toString());
		} catch (NumberFormatException e) {
			return false;
		}
		return true;

	}
	
	/**
	 * 次のトークンが指定されたものかをチェックする。
	 *
	 * 空白文字（スペース、タブ、改行、コメント）だったらスキップして
	 * その次のものをチェックする。
	 *
	 * @param int val 検出すべきトークン種別
	 * @return boolean 指定されたトークンだったらtrue、違っていたらfalse
	 */
	protected boolean checkToken(int val)throws IOException{
		
		int token;
		while ((token = m_tokenizer.nextToken()) != StreamTokenizer.TT_EOF) {
			//Ver3.2 コメント対応
			if(token == '-'){	
				if( forwardLineComment() ){
					//コメントを読み飛ばしたら次の文字
					continue;				
				}
			}else if(token == '/'){
				if( forwardComment() ){
					//コメントを読み飛ばしたら次の文字
					continue;				
				}
			}

			if(token == ' ' ||
			   token == '\t' ||
			   token == '\r' ||
			   token == '\n'   ){
				continue;											
			}else if (token == val){
				return true;
			}else {
				break;
			}								
		}
		return false;							

	}

	/**
	 * HAVINGの有無
	 * @return true -あり
	 *          false -なし
	 * @since 3.0
	 */
	public boolean hasHaving() {
		return m_having;
	}

	public String getSrcSql() {
		return m_srcSql;
	}
	

	public void setParamList(ParameterList parameters) {
		
		if(parameters == null){
			return;
		}
		
		for (Iterator iter = m_paramList.iterator(); iter.hasNext();) {
			ParamValue paramValue = (ParamValue) iter.next();
			int paramIndex = paramValue.getParamIndex();
			paramValue.setValue(parameters.toString(paramIndex));
		}
	
	
	}

	public int getParamNum() {
		return m_tokenizer.getParamNum();
	}

	public String toString(ParameterList parameters) {
	  ArrayList fragments = m_tokenizer.getFragmentList();
	  
      StringBuffer sbuf = new StringBuffer((String)fragments.get(0));
      for (int i = 1; i < fragments.size(); ++i)
      {
          if (parameters == null)
              sbuf.append('?');
          else
              sbuf.append(parameters.toString(i));
          sbuf.append((String)fragments.get(i));
      }
      return sbuf.toString();
  }

}
