/**
 * PQConnection JS 클래스 리스트
 * 
 * PQConnectionInputInfo
 * PQConnectionInput
 * PQConnectionModel
 * PQConnectionSearchInfo
 * PQConnectionSearch
 * 
 * PQOperation
 * PQOPCompiler
 * PQOPCalculate
 * PQOPInfo
 * PQOPAtom
 * PQOPInfoManager
 * PQOPDefine
 */

///////////////////////
// 입력란 연결

function PQConnectionInputInfo (strVarName, strTableName, strFieldName, strValue)
{
	this.m_strVarName = strVarName;
	this.m_strTableName = strTableName;
	this.m_strFieldName = strFieldName;
	this.m_strValue =  strValue;
}
	
PQConnectionInputInfo.prototype.getVarName = function ()
{
	return this.m_strVarName;
}

PQConnectionInputInfo.prototype.getTableName = function ()
{
	return this.m_strTableName;
}

PQConnectionInputInfo.prototype.getFieldName = function ()
{
	return this.m_strFieldName;
}

PQConnectionInputInfo.prototype.getValue = function ()
{
	return this.m_strValue;
}

function PQConnectionInput ()
{
}

PQConnectionInput.prototype.execute = function (arConInputInfo)
{
	var bInquiryAction = false;
	
	for (var i = 0, nLen = arConInputInfo.length; i < nLen; i++)
	{
		var objConInputInfo = arConInputInfo[i];
		
		var strVarName = objConInputInfo.getVarName();
		var strTableName = objConInputInfo.getTableName();
		var strFieldName = objConInputInfo.getFieldName();
		var strValue = objConInputInfo.getValue();
		
		var objAtom = this._getAtom(strVarName, strTableName, strFieldName);
		if (null != objAtom)
		{
			objAtom.setValue(strValue, Define.OCCUR_CHANGE_FLAG);
			
			// 조회동작을 할지 여부
			if (!bInquiryAction)
			{
				bInquiryAction = this.isInquiryAction(objAtom);
			}
		}
	}
	
	if (bInquiryAction)
	{
		this._inquiryAction();
	}
}

/**
 * @param objAtom 아톰
 * @return 아톰 또는 아톰을 연산식으로 사용하는 다른 아톰이 기본검색키인지 여부
 */
PQConnectionInput.prototype.isInquiryAction = function (objAtom)
{
	if (objAtom.isDefaultKey())
	{
		return true;
	}
	
	if (objAtom.isOperation())
	{
		var arRefAtomVarNameList = PQOperation.getRefOpAtomList(objAtom.getVarName());
		
		for (var i = 0; i < arRefAtomVarNameList.length; i += 1)
		{
			var strRefAtomVarName = arRefAtomVarNameList[i];
			var objRefAtom = PQOperation.getAtom(strRefAtomVarName, objAtom.getRowIndex());
			
			if (null != objRefAtom)
			{
				return this.isInquiryAction(objRefAtom);
			}
		}
	}
	
	return false;
}

PQConnectionInput.prototype._getAtom = function (strVarName, strTableName, strFieldName)
{
	var objInputDataAtom = this._getInputDataAtom(strVarName, strTableName, strFieldName);		
	
	if (null != objInputDataAtom)
	{
		return objInputDataAtom;
	}
	
	var objTimeDataAtom = this._getInputTimeAtom(strVarName, strTableName, strFieldName);
	
	if (null != objTimeDataAtom)
	{
		return objTimeDataAtom;
	}
	
	return null;
}

/*
* 1. 변수명, 2.테이블명과 필드명으로 해당 데이터입력란을 반환한다.
*/
PQConnectionInput.prototype._getInputDataAtom = function (strVarName, strTableName, strFieldName)
{
	var objAtom = null;
	if (ContainsInputDataAtom())
	{	
		objAtom = InputDataAtom.getAtom(strVarName);
		
		if (null == objAtom)
		{
			objAtom = InputDataAtom.getAtomByDatabaseInfo(strTableName, strFieldName);
		}
	}

	return objAtom;
}

/*
* 1. 변수명, 2.테이블명과 필드명으로 해당 데이터입력란을 반환한다.
*/
PQConnectionInput.prototype._getInputTimeAtom = function (strVarName, strTableName, strFieldName)
{
	var objAtom = null;
	if (ContainsInputTimeAtom())
	{	
		objAtom = InputTimeAtom.getAtom(strVarName);
		
		if (null == objAtom)
		{
			objAtom = InputTimeAtom.getAtomByDatabaseInfo(strTableName, strFieldName);
		}
	}

	return objAtom;
}

PQConnectionInput.prototype._inquiryAction = function ()
{
	if (g_bQWPClientMode)
	{
		g_objDoc.loadData(true, SQL_RECORD_TYPE.EQUAL_RECORD);
	}
	else
	{
		GlobalField.setServiceName("InquiryAction");
		GlobalField.setServiceEventName("Normal");
		
		var xnRequestDoc = this._makeRequest();
		var xnResultDoc = PQService.executeService(xnRequestDoc);
		HandleResult.execute(xnResultDoc);
	}
	
	// 폼_검색완료(#지금)
	ScriptFormEvent.onEndSearch(ScriptEvent.SEARCH_END, ScriptEvent.SEARCH_SUCCEED);
}

PQConnectionInput.prototype._makeRequest = function ()
{
	var xnRequest = MakeRequest.createRequestNode();		
	MakeRequest.makeServiceRequest(xnRequest);
	
	var xnAtomRequest = MakeRequest.getAtomRequestNode(xnRequest);		
	
	// 기본검색키 요청만 생성하면, 팝업에 의해 설정된 값이 요청에 포함되지 않아
	// handle 과정에서 refresh 되어 사라지는 문제 때문에, 전체 아톰 요청 보내도록 수정... -_-;
	if (ContainsInputDataAtom())
	{
		InputDataAtom.makeRequest(xnAtomRequest);
	}
	if (ContainsInputTimeAtom())
	{
		InputTimeAtom.makeRequest(xnAtomRequest);
	}
	if (ContainsInputImageAtom())
	{
		// 이미지 입력란은 기본검색키가 아니더라도,
		// 다른 기본검색키 입력란의 값이 바뀌면, 이미지를 update하기 위해 요청에 포함
		InputImageAtom.makeRequest(xnAtomRequest);
	}
	if (ContainsComboAtom())
	{
		ComboAtom.makeRequest(xnAtomRequest);
	}
	if (ContainsCheckAtom())
	{
		CheckAtom.makeRequest(xnAtomRequest);
	}
	if (ContainsRadioAtom())
	{
		RadioAtomGroup.makeRequest(xnAtomRequest);
	}
	if (ContainsAttachAtom())
	{
		AttachAtom.makeRequest(xnAtomRequest);
	}
	if (ContainsWebDHtmlEditAtom())
	{
		WebDHtmlEditAtom.makeRequest(xnAtomRequest);
	}
	if (ContainsWebFileAttachAtom())
	{
		WebFileAttachAtom.makeRequest(xnAtomRequest);
	}
	
	return xnRequest.ownerDocument;
}


PQConnectionInput.init = function ()
{
	g_objPQConnectionInput = new PQConnectionInput();
}

PQConnectionInput.execute = function (arConInputInfo)
{
	if (null != g_objPQConnectionInput)
	{
		g_objPQConnectionInput.execute(arConInputInfo);
	}
}

function PQConnectionModel ()
{
	this.m_strCurrentOpenModelRegisterNumber = "";
	this.m_bCurrentOpenModelUseDmt = false;
}

///////////////////////////////////////////
// public method
//

PQConnectionModel.prototype.getChildModelRegisterNumber = function ()
{
	return this.m_strCurrentOpenModelRegisterNumber;
}

PQConnectionModel.prototype.getChildModelUseDmt = function ()
{
	return this.m_bCurrentOpenModelUseDmt;
}

/**
 * 상세폼을 연다.
 * @param strModelName 모델명.
 * @param strRegisterNumber 결재 등록번호.
 * @param bUseDmt TopDmt 사용 여부.
 * @param bToaSave 결재 완료 문건은 true.
 */
PQConnectionModel.prototype.execute = function (strModelName, strRegisterNumber, bUseDmt, bToaSave)
{
	if (!strModelName)
	{
		if (bUseDmt)
		{
			if (!strRegisterNumber && !strModelName)
			{
				return false;
			}

			var objPQClient = Utils.findFrame("__PQClient_Frame", window.parent).document.getElementById("__PQClient");
			
			strModelName = objPQClient.getModelNameWithRegisterNumber(strRegisterNumber, bToaSave);
		}
		// 일반 모델에서 모델이름이 없는 경우는 에러
		else
		{
			return false;
		}
	}
	
	strModelName = Utils.removePQPath(strModelName);
	
	this._setChildModelRegisterNumber(strRegisterNumber);
	this._setChildModelUseDmt(bUseDmt);
	
	// PQ Build 에서 상세폼 연결은 부모-자식폼 관계가 없음.
	// 그래서 스크립트의 객체생성 루틴을 호출하지 않고 Window open 함.
	var strURL = "";
	if (0 < strModelName.indexOf(".QRP"))
	{
		strURL = "/ups/Report.html?ModelName=";
	}
	// .QPG, QFR, DMT 등
	else
	{
		strURL = "/ups/Model.html?ModelName=";
	}
	
	if (bUseDmt)
	{
		strURL += strModelName;
	}
	else
	{
		strURL += GetProjectName() + "/" + strModelName;
	}
	Utils.openWindow(strURL, 1);

	return true;
}

/**
 * 모델 열기, 스케줄러에서 호출합니다. 다른곳에는 사용금지
 */
PQConnectionModel.prototype.openModel = function (strModelName, strRegisterNumber, bUseDmt)
{
	this._setChildModelRegisterNumber(strRegisterNumber);
	this._setChildModelUseDmt(bUseDmt);
	
	if (bUseDmt)
	{
		var strURL = "/ups/Model.html?ModelName=" + strModelName;
	}
	else
	{
		// 문서모델  열기, 스케줄러가 포함된 웹모델에서 열리므로 스크립트의 자식폼열기로 열지 않고 바로 연다.
		var strURL = "/ups/Model.html?ModelName=" + GetProjectName() + "/" + strModelName;
	}
	
	Utils.openWindow(strURL, 1);
	
	return true;
}

PQConnectionModel.prototype._setChildModelRegisterNumber = function (strRegisterNumber)
{
	this.m_strCurrentOpenModelRegisterNumber = strRegisterNumber;
}

PQConnectionModel.prototype._setChildModelUseDmt = function (bUseDmt)
{
	this.m_bCurrentOpenModelUseDmt = bUseDmt;
}

PQConnectionModel.prototype._getDocumentNumberWithResisterNumber = function (strRegisterNumber)
{
	
}


////////////////////////////////
// static method
//

PQConnectionModel.init = function ()
{
	g_objPQConnectionModel = new PQConnectionModel();
}


/**
 * 상세폼 열기 - 검색창, 스크립트에서 이용.
 * @param strExeVarNames 모델 이름
 * @param heConnectionModelInfo 값을 넘겨줄 아톰-값 정보
 */
PQConnectionModel.execute = function (strModelName, htConnectionModelInfo, bBpmModel, objBpmExecuteInfo, objContents)
{
	GlobalField.setConnectionModelInfo(htConnectionModelInfo);
	
	if (bBpmModel)
	{
		//BpmExecuteInfo를 스케줄러 GlobalField.BpmExecuteInfoList에 등록한다.
		var htBpmExecuteInfoList = GlobalField.getBpmExcuteInfoList();
		var htBpmContentsList = GlobalField.getBpmContentList();
		
		htBpmExecuteInfoList[strModelName] = objBpmExecuteInfo;
		htBpmContentsList[strModelName] = objContents;
		
		// 모델 열기, BPM은 스케줄러가 포함된 웹모델에서 열리므로 스크립트의 자식폼열기로 열지 않고 바로 연다.
		var strURL = "/ups/Model.html?ModelName=" + GetProjectName() + "/" + strModelName;
		return Utils.openWindow(strURL);
	}
	else
	{
		return g_objPQConnectionModel.execute(strModelName, "", false);
	}
}

/**
 * 문서모델을 결재등록번호를 이용해서 연다
 * @param strRegisterNumber 결재 등록 번호
 */
PQConnectionModel.executeNtoaModelWithRegisterNumber = function (strRegisterNumber, bToaSave)
{
	g_objPQConnectionModel.execute("", strRegisterNumber, true, bToaSave);
}

/**
 * 문서모델을 문서서식번호를 이용해서 연다
 * @param strDocumentNumber 문서 서식 번호
 */
PQConnectionModel.executeNtoaModelWithDocumentNumber = function (strDocumentNumber)
{
	g_objPQConnectionModel.execute(strDocumentNumber, "", true);
}

/**
 * 연결 모델에 넘길 데이터를 만든다.
 * @param strExeVarNames 실행변수명.
 */
PQConnectionModel.makeSendedData = function (strExeVarNames)
{
	var htConnectionModelInfo = new Hashtable();
	strExeVarNames = strExeVarNames.replace(new RegExp(" ", "g"), ""); 
	
	// PQ 5.0 - 검색창@항목변수 -> 검색창.항목변수
	strExeVarNames = Utils.replace(strExeVarNames, "\\.", "@");
	// PQ 5.0 - 부모폼입력란$자식폼입력란 -> 부모폼입력란=자식폼입력란
	strExeVarNames = Utils.replace(strExeVarNames, "=", "$");

	var arExeVarList = strExeVarNames.split(",");	
	var nLen = arExeVarList.length;
	for (var i = 0; i < nLen; i++)
	{
		var strExeVar = arExeVarList[i];
		
		var arItems = strExeVar.split("@");
		var arDestItem;
		
		var strVarName = "";
		var strValue = "";
		var strFieldName = "";
		
		// 일반 입력란
		if (1 == arItems.length)
		{
			arDestItem = arItems[0].split("$");
			strVarName = arDestItem[0];
			
			var objAtom = null;
			if (ContainsInputDataAtom())
			{
				objAtom = InputDataAtom.getAtom(strVarName);
			}
			if (null == objAtom && ContainsInputTimeAtom())
			{
				objAtom = InputTimeAtom.getAtom(strVarName);
			}
			if (null == objAtom && ContainsComboAtom())
			{
				objAtom = ComboAtom.getAtom(strVarName);
			}
			
			// 스크롤의 변수명만 참조변수명으로 설정되었을 경우에도 값을 전달하도록 변경함
			if (null == objAtom && ContainsScrollAtom())
			{
				objAtom = ScrollAtom.findBindedAtom(strVarName);
			}
			
			if (null != objAtom)
			{
				strValue = objAtom.getValue();
				
				if (objAtom.getPrefix)
				{
					strValue = objAtom.getPrefix() + strValue;
				}
				
				strFieldName = objAtom.getFieldName();
				
				if (2 == arDestItem.length)		// 상세폼의 변수명이 지정된 경우
				{
					strVarName = arDestItem[1];
				}
			
				htConnectionModelInfo[strVarName] = new Array(strValue, strFieldName);
			}
			else if (ContainsReportBrowseAtom())
			{
				objAtom = ReportBrowseAtom.getAtom(strVarName);
				
				// 검색창 있을 경우 값 전달
				if (null != objAtom)
				{
					htConnectionModelInfo = objAtom._makeConnectionModeInfoByItemValue(htConnectionModelInfo);
				}
			}
		}
		// 스크롤에 묶인 입력란
		else if (2 == arItems.length)
		{
			var strScrollName = arItems[0];

			arDestItem = arItems[1].split("$");
			strVarName = arDestItem[0];
			
			var objScrollAtom = null;
			if (ContainsScrollAtom())
			{
				objScrollAtom = ScrollAtom.getAtom(strScrollName);
			}
			
			if (null != objScrollAtom)
			{
				var strPrefix = objScrollAtom.getSelectBindedAtomPrefix(strVarName);
				strValue = strPrefix + objScrollAtom.getSelectBindedAtomValue(strVarName);
				
				strFieldName = objScrollAtom.getSelectBindedAtomFieldName(strVarName);
				
				if (2 == arDestItem.length)		// 상세폼의 변수명이 지정된 경우
				{
					strVarName = arDestItem[1];
				}
				
				htConnectionModelInfo[strVarName] = new Array(strValue, strFieldName);
			}
		}
	}
	
	return htConnectionModelInfo;
}

/**
 * 출력물에 넘길 데이터를 만든다.
 *
 * 출력물에 넘길 때는 필드명을 포함시키지 않는다.
 * @param strExeVarNames 실행변수명.
 */
PQConnectionModel.makeSendedDataToReport = function (strExeVarNames)
{
	var htConnectionModelInfo = new Hashtable();
	strExeVarNames = strExeVarNames.replace(new RegExp(" ", "g"), ""); 
	
	// PQ 5.0 - 검색창@항목변수 -> 검색창.항목변수
	strExeVarNames = Utils.replace(strExeVarNames, "\\.", "@");
	// PQ 5.0 - 부모폼입력란$자식폼입력란 -> 부모폼입력란=자식폼입력란
	strExeVarNames = Utils.replace(strExeVarNames, "=", "$");

	var arExeVarList = strExeVarNames.split(",");	
	var nLen = arExeVarList.length;
	for (var i = 0; i < nLen; i++)
	{
		var strExeVar = arExeVarList[i];
		
		var arItems = strExeVar.split("@");
		var arDestItem;
		
		var strVarName = "";
		
		// 일반 입력란
		if (1 == arItems.length)
		{
			var strValue = "";
			
			arDestItem = arItems[0].split("$");
			strVarName = arDestItem[0];
			
			var objAtom = null;
			if (ContainsInputDataAtom())
			{
				objAtom = InputDataAtom.getAtom(strVarName);
			}
			if (null == objAtom && ContainsInputTimeAtom())
			{
				objAtom = InputTimeAtom.getAtom(strVarName);
			}
			
			// 스크롤의 변수명만 참조변수명으로 설정되었을 경우에도 값을 전달하도록 변경함
			if (null == objAtom && ContainsScrollAtom())
			{
				var objBindAtom = ScrollAtom.findBindedAtom(strVarName);
				if (null != objBindAtom)
				{
					strValue = objBindAtom.getValue();
				}
			}
			if (null != objAtom)
			{
				strValue = objAtom.getValue();
				
				if (objAtom.getPrefix)
				{
					strValue = objAtom.getPrefix() + strValue;
				}
				
				if (2 == arDestItem.length)		// 상세폼의 변수명이 지정된 경우
				{
					strVarName = arDestItem[1];
				}
			
				htConnectionModelInfo[strVarName] = new Array(strValue);
			}
			else if (ContainsReportBrowseAtom())
			{
				objAtom = ReportBrowseAtom.getAtom(strVarName);
				
				// 검색창 있을 경우 값 전달
				if (null != objAtom)
				{
					htConnectionModelInfo = objAtom._makeConnectionModeInfoByItemValue(htConnectionModelInfo);
				}
			}
		}
		// 스크롤에 묶인 입력란 또는 검색창 항목명
		else if (2 == arItems.length)
		{
			var arValue = null;
			var strScrollName = arItems[0];

			arDestItem = arItems[1].split("$");
			strVarName = arDestItem[0];
			
			var objScrollAtom = null;
			if (ContainsScrollAtom())
			{
				objScrollAtom = ScrollAtom.getAtom(strScrollName);
				
				if (null != objScrollAtom)
				{
					// 값들의 array를 리턴
					arValue = objScrollAtom.getAllBindedAtomValue(strVarName);
				}
			}
			if (ContainsReportBrowseAtom())
			{
				objScrollAtom = ReportBrowseAtom.getAtom(strScrollName);
				
				if (null != objScrollAtom)
				{
					// 값들의 array를 리턴
					arValue = objScrollAtom.getItemArrayValue(strVarName);
				}
			}
			
			if (2 == arDestItem.length)		// 상세폼의 변수명이 지정된 경우
			{
				strVarName = arDestItem[1];
			}
			htConnectionModelInfo[strVarName] = arValue;
		}
	}
	
	return htConnectionModelInfo;
}

/**
 * 상세폼 열기 - 입력란, 버튼, 팝업에서 이용.
 *
 * @param strExeVarNames 실행변수명
 * @param strConnectModel 연결모델명
 */
PQConnectionModel.open = function (strConnectionModelName, strExeVarNames)
{
	var htConnectionModelInfo = PQConnectionModel.makeSendedData(strExeVarNames);
	
	PQConnectionModel.execute(strConnectionModelName, htConnectionModelInfo, false, null);
}

// 출력물 열기
PQConnectionModel.openReport = function (strConnectionModelName, strExeVarNames)
{
	var htConnectionModelInfo = PQConnectionModel.makeSendedDataToReport(strExeVarNames);
	
	PQConnectionModel.execute(strConnectionModelName, htConnectionModelInfo, false, null);
}

/**
 * BPM 모델 열기, 스케줄러에서 호출합니다. 다른곳에는 사용금지
 *
 * @param strExeVarNames 기본 설정 값 (변수명$값,변수병$값,변수명$값...)
 * @param strConnectModel 연결모델명
 */
PQConnectionModel.openBpmModel = function (strConnectionModelName, strExeVarNames, objBpmExecuteInfo, objContents)
{
	var debug = false;
	var htBPMModelModelInfo = new Hashtable();
	strExeVarNames = strExeVarNames.replace(new RegExp(" ", "g"), ""); 
	var arExeVarList = strExeVarNames.split(",");
	
	var nLen = arExeVarList.length;
	
	for (var i = 0; i < nLen; i++)
	{
		var strExeVar = arExeVarList[i];
		
		var arItems = strExeVar.split("$");

		htBPMModelModelInfo[arItems[0]] = arItems[1];
	}
	
	PQConnectionModel.execute(strConnectionModelName, htBPMModelModelInfo, true, objBpmExecuteInfo, objContents);
}

/**
 * 메일 읽기, 스케줄러에서 호출합니다. 다른곳에는 사용금지
 */
PQConnectionModel.openMailModel = function (htConnectionModelInfo)
{
	GlobalField.setConnectionModelInfo(htConnectionModelInfo);
	
	// 메일읽기 모델  열기, 메일읽기 모델은  스케줄러가 포함된 웹모델에서 열리므로 스크립트의 자식폼열기로 열지 않고 바로 연다.
	var strURL = "/ups/Model.html?ModelName=sys/etc/CMailRcvOrign.QPG";
	return Utils.openWindow(strURL, 1);
}

/**
 * 공지사항  읽기, 스케줄러에서 호출합니다. 다른곳에는 사용금지
 */
PQConnectionModel.openBulletinModel = function (htConnectionModelInfo)
{
	GlobalField.setConnectionModelInfo(htConnectionModelInfo);
	
	// 공지읽기 모델  열기, 공지읽기 모델은  스케줄러가 포함된 웹모델에서 열리므로 스크립트의 자식폼열기로 열지 않고 바로 연다.
	var strURL = "/ups/Model.html?ModelName=sys/etc/1_APubNotifyListChild[READONLY].QPG";
	return Utils.openWindow(strURL, 1);
}

/**
 * 모델 열기, 스케줄러에서 호출합니다. 다른곳에서는 사용금지
 */
PQConnectionModel.openModel = function (strModelName)
{
	if (0 < strModelName.toUpperCase().indexOf(".QPG"))
	{
		g_objPQConnectionModel.openModel(strModelName, "", false);
	}
	else
	{
		g_objPQConnectionModel.openModel(strModelName, "", true);
	}
}

/**
 * 문서모델 열기, 스케줄러에서 호출합니다. 다른곳에서ㄴ는 사용금지
 */
PQConnectionModel.openNtoaModel = function (htConnectionModelInfo)
{
	GlobalField.setConnectionModelInfo(htConnectionModelInfo);
	
	var strFormCode = htConnectionModelInfo["P09"][0];
	var strRegisteredNum = htConnectionModelInfo["D01"][0];
	
	g_objPQConnectionModel.openModel(strFormCode, strRegisteredNum, true);
}


PQConnectionModel.getChildModelName = function ()
{
	return g_objPQConnectionModel.getChildModelName();
} 

PQConnectionModel.getChildModelUseDmt = function ()
{
	return g_objPQConnectionModel.getChildModelUseDmt();
}

PQConnectionModel.getChildModelRegisterNumber = function ()
{
	return g_objPQConnectionModel.getChildModelRegisterNumber();
}


function PQConnectionSearchInfo (strVarName, strType)
{
	////////////////////
	// private member
	
	this.m_strVarName = strVarName;		// 연결검색이 설정된 아톰의 변수명
	
	/**
	 * radio
	 * check
	 * combo
	 * tree
	 * browse
	 */
	this.m_strType = strType;			// 연결검색이 설정된 아톰의 타입
	
	this.m_strConnectionVarName = "";	// 연결검색을 수행할 검색 아톰 변수명
	this.m_strItemVarName = "";				// 항목변수
}


PQConnectionSearchInfo.prototype.getVarName = function ()
{
	return this.m_strVarName;
}

PQConnectionSearchInfo.prototype.getConnectionVarName = function ()
{
	return this.m_strConnectionVarName;
}

PQConnectionSearchInfo.prototype.getItemVarName = function ()
{
	return this.m_strItemVarName;
}

PQConnectionSearchInfo.prototype.getType = function ()
{
	return this.m_strType;
}


////////////////////
// public method

/**
 * 연결검색 정보를 설정한다.
 * 
 * @strRefVarName 참조변수명
 */
PQConnectionSearchInfo.prototype.init = function (strRefVarName)
{
	var arRefInfo = strRefVarName.split(",");
	
	for (var i = 0; i < arRefInfo.length; i++)
	{
		var strRefInfo = arRefInfo[i];
		
		strRefInfo = strRefInfo.replace("#LOAD", "");
		strRefInfo = strRefInfo.replace("@연결검색", "");
		
		// PQ 5.0 - 검색창:항목변수 -> 검색창.항목변수
		strRefInfo = Utils.replace(strRefInfo, "\\.", ":");		
		
		var strConnInfos = strRefInfo.split(":");
		
		if (2 == strConnInfos.length)
		{
			this.m_strConnectionVarName = strConnInfos[0];
			this.m_strItemVarName = strConnInfos[1];
			
			break;
		}
	}
}


////////////////////
// static member
	
PQConnectionSearchInfo.RADIO = "radio";
PQConnectionSearchInfo.CHECK = "CheckAtom";
PQConnectionSearchInfo.COMBO = "combo";
PQConnectionSearchInfo.TREE = "tree";
PQConnectionSearchInfo.BROWSE = "browse";
PQConnectionSearchInfo.GRIDEX = "gridex";
PQConnectionSearchInfo.WEBBOARD = "webboard";


function PQConnectionSearch (arConnSearchInfoList)
{
	////////////////////
	// private member
	
	this.m_arConnSearchInfoList = arConnSearchInfoList;	// 연결검색 정보 리스트
}


PQConnectionSearch.prototype.execute = function (strVarName)
{
	var strConnectionVarName = this._getConnectionVarName(strVarName);
	if (null == strConnectionVarName || 0 == strConnectionVarName.length)
	{
		return;
	}
	
	if (ContainsReportBrowseAtom())
	{
		var objAtom = ReportBrowseAtom.getAtom(strConnectionVarName);
		
		if (null != objAtom)
		{
			objAtom.executeConnectionSearch();
		}
	}
	
	if (ContainsTreeAtom())
	{
		var objAtom = TreeAtom.getAtom(strConnectionVarName);
		
		if (null != objAtom)
		{
			objAtom.executeConnectionSearch();
		}
	}
	
	if (ContainsGridExAtom())
	{
		var objAtom = GridExAtom.getAtom(strConnectionVarName);
		
		if (null != objAtom)
		{
			objAtom.executeConnectionSearch();
		}
	}
	
	if (ContainsWebBoardAtom())
	{
		var objAtom = WebBoardAtom.getAtom(strConnectionVarName);
		
		if (null != objAtom)
		{
			objAtom.executeConnectionSearch();
		}
	}
}

PQConnectionSearch.prototype.makeWhereClause = function (strConnectionVarName)
{
	return this._makeWhereClause(strConnectionVarName);
}


////////////////////////////////////
// private method
//

PQConnectionSearch.prototype._getConnectionVarName = function (strVarName)
{
	var arConnSearchInfoList = this.m_arConnSearchInfoList;
	if (null != arConnSearchInfoList)
	{
		var nLen = arConnSearchInfoList.length;
		for (var i = 0; i < nLen; i++)
		{
			if (strVarName == arConnSearchInfoList[i].getVarName())
			{
				return arConnSearchInfoList[i].getConnectionVarName();
			}
		}
	}
	
	return "";
}

PQConnectionSearch.prototype._makeWhereClause = function (strConnectionVarName)
{
	var strWhere = "";
	var arCheckList = new Array();
	
	var arConnSearchInfoList = this.m_arConnSearchInfoList;
	var nLen = arConnSearchInfoList.length;
	for (var i = 0; i < nLen; i++)
	{
		if (strConnectionVarName == arConnSearchInfoList[i].getConnectionVarName())
		{
			var objConnInfo = arConnSearchInfoList[i];
			var strType = objConnInfo.getType();
			
			if (PQConnectionSearchInfo.RADIO == strType)
			{
				strWhere = this._makeWhereClauseOfRadio(strWhere, objConnInfo);
			}
			else if (PQConnectionSearchInfo.CHECK == strType)
			{
				arCheckList[arCheckList.length] = objConnInfo;
			}
			else if (PQConnectionSearchInfo.COMBO == strType)
			{
				strWhere = this._makeWhereClauseOfCombo(strWhere, objConnInfo);
			}
			else if (PQConnectionSearchInfo.TREE == strType)
			{
				strWhere = this._makeWhereClauseOfTree(strWhere, objConnInfo);
			}
			else if (PQConnectionSearchInfo.BROWSE == strType)
			{
				strWhere = this._makeWhereClauseOfBrowse(strWhere, objConnInfo);
			}
			else if (PQConnectionSearchInfo.GRIDEX == strType)
			{
				strWhere = this._makeWhereClauseOfGridEx(strWhere, objConnInfo);
			}
		}
	}
	
	var strCheckWhere = this._makeWhereClauseOfCheck(arCheckList);
	if (null != strCheckWhere && 0 < strCheckWhere.length)
	{
		strWhere = this._makeCompleteWhereClause(strWhere, strCheckWhere);
	}
	
	return strWhere;
}

/**
 * 라디오 버튼의 연결검색 조건문을 구성한다.
 * 
 * @strWhere
 * @objConnInfo
 *
 * @return strWhere
 */
PQConnectionSearch.prototype._makeWhereClauseOfRadio = function (strWhere, objConnInfo)
{
	var strVarName = objConnInfo.getVarName();
	
	var objAtom = null;
	if (ContainsRadioAtom())
	{
		objAtom = RadioAtom.getAtom(strVarName);
	}
	
	if (!objAtom.isChecked())
	{
		return strWhere;
	}
	
	var strValue = objAtom.getValue();
	if (0 == strValue.length || "#TOTAL" == strValue || "전체" == strValue)
	{
		return strWhere;
	}
	
	var strFieldName = this._getConnectionFieldName(objConnInfo);
	var strItemVarName = objConnInfo.getItemVarName();
	
	return this._concatWhereClause(strWhere, strFieldName, strValue, strItemVarName, " LIKE ", " AND ");
}

/**
 * 콤보박스의 연결검색 조건문을 구성한다.
 * 
 * @strWhere
 * @objConnInfo
 *
 * @return strWhere
 */
PQConnectionSearch.prototype._makeWhereClauseOfCombo = function (strWhere, objConnInfo)
{
	if (ContainsComboAtom())
	{
		var strVarName = objConnInfo.getVarName();
		
		var objAtom = ComboAtom.getAtom(strVarName);
		
		var strFieldName = this._getConnectionFieldName(objConnInfo);
		var strValue = objAtom.getValue();
		var strItemVarName = objConnInfo.getItemVarName();
		
		if (0 == strValue.length || " " == strValue)
		{
			return strWhere;
		}
		
		if (objAtom.isMemoryCombo())
		{
			// 자체정의 콤보일 경우
			var nIndex = strValue.indexOf('$');
			if (nIndex < 0)
			{
				nIndex = strValue.indexOf(',');
			}
			
			if (0 < nIndex)
			{
				strValue = strValue.substring(0, nIndex);
			}
		}
		
		return this._concatWhereClause(strWhere, strFieldName, strValue, strItemVarName, " LIKE ", " AND ");
	}
	else
	{
		return;
	}
}

/**
 * 체크박스의 연결검색 조건문을 구성한다.
 * 
 * @arCheckList
 *
 * @return strCheckWhere
 */
PQConnectionSearch.prototype._makeWhereClauseOfCheck = function (arCheckList)
{
	var strCheckWhere = "";
	
	var nLen = arCheckList.length;
	for (var i = 0; i < nLen; i++)
	{
		var objConnInfo = arCheckList[i];
		var strItemVarName = objConnInfo.getItemVarName();
		if (null == objConnInfo)
		{
			continue;
		}
		
		var strVarName = objConnInfo.getVarName();
		
		var objAtom = null;
		if(ContainsCheckAtom())
		{
			objAtom = CheckAtom.getAtom(strVarName);
		}
		if (null == objAtom || objAtom.isUnChecked())
		{
			continue;
		}
		
		var strFieldName = this._getConnectionFieldName(objConnInfo);
		var strValue = objAtom.getValue();
		
		strCheckWhere = this._concatWhereClause(strCheckWhere, strFieldName, strValue, strItemVarName, " LIKE ", " OR ");
	}
	
	return strCheckWhere;
}

/**
 * 트리의 연결검색 조건문을 구성한다.
 * 
 * @strWhere
 * @objConnInfo
 *
 * @return strWhere
 */
PQConnectionSearch.prototype._makeWhereClauseOfTree = function (strWhere, objConnInfo)
{
	var strVarName = objConnInfo.getVarName();
	var objAtom = TreeAtom.getAtom(strVarName);
		
	var strFieldName = this._getConnectionFieldName(objConnInfo);
	
	var strItemVarName = objConnInfo.getItemVarName();
	var strValue = objAtom.getColumnValue(strItemVarName);

	if (null == strValue)
	{
		strValue = "%";
	}
	else
	{
		strValue += "%";
	}
	
	var strLevelChar = objAtom.getLevelChar();
	
	var strRegExpr = this._getLevelCharRegExprString(strLevelChar);
	var objRegExpr = new RegExp(strRegExpr, "g");
	
	strValue = strValue.replace(objRegExpr, "%");
	
	strWhere = this._concatWhereClause(strWhere, strFieldName, strValue, strItemVarName, " LIKE ", " AND ");
	
	return strWhere;
}

/**
 * 검색창의 연결검색 조건문을 구성한다.
 * 
 * @strWhere
 * @objConnInfo
 *
 * @return strWhere
 */
PQConnectionSearch.prototype._makeWhereClauseOfBrowse = function (strWhere, objConnInfo)
{
	if (ContainsReportBrowseAtom())
	{
		var strVarName = objConnInfo.getVarName();
		var objAtom = ReportBrowseAtom.getAtom(strVarName);
		
		var strFieldName = this._getConnectionFieldName(objConnInfo);
		
		var strItemVarName = objConnInfo.getItemVarName();
		var strValue = objAtom.getColumnValue(strItemVarName);
		return this._concatWhereClause(strWhere, strFieldName, strValue, strItemVarName, " LIKE ", " AND ");
	}
	else
	{
		return;
	}
}

PQConnectionSearch.prototype._makeWhereClauseOfGridEx = function (strWhere, objConnInfo)
{
	if (ContainsGridExAtom())
	{
		var strVarName = objConnInfo.getVarName();
		var objAtom = GridExAtom.getAtom(strVarName);
		
		var strFieldName = this._getConnectionFieldName(objConnInfo);
		
		var strItemVarName = objConnInfo.getItemVarName();
		var strValue = objAtom.getItemValue(strItemVarName);
		return this._concatWhereClause(strWhere, strFieldName, strValue, strItemVarName, " LIKE", " AND ");
	}
	
	return;
}

PQConnectionSearch.prototype._getConnectionFieldName = function (objConnInfo)
{
	if (ContainsReportBrowseAtom())
	{
		var strConnectionVarName = objConnInfo.getConnectionVarName();
		var strItemName = objConnInfo.getItemVarName();
	
		var objAtom = ReportBrowseAtom.getAtom(strConnectionVarName);
	
		return objAtom.getTableFieldName(strItemName);
	}
	else if (ContainsGridExAtom() && PQConnectionSearchInfo.GRIDEX == objConnInfo.getType())
	{
		var strConnectionVarName = objConnInfo.getConnectionVarName();
		var strItemName = objConnInfo.getItemVarName();
		
		var objAtom = GridExAtom.getAtom(strConnectionVarName);
		
		return objAtom.getTableFieldName(strItemName);
	}
	else if (ContainsWebBoardAtom())
	{
		var strConnectionVarName = objConnInfo.getConnectionVarName();
		var strItemName = objConnInfo.getItemVarName();
		
		var objAtom = WebBoardAtom.getAtom(strConnectionVarName);
		
		return objAtom.getTableFieldName(strItemName);
	}
	else
	{
		return;
	}
}

/**
 * 트리 단계 구성 문자로 검색조건 정규 표현식을 만든다.
 * 
 * @strLevelChar
 *
 * @return strRegExpr
 */
PQConnectionSearch.prototype._getLevelCharRegExprString = function (strLevelChar)
{
	// 정규 표현식
	// /정규표현식/flag
	// \*+ : 1번이상 반복(+)하는 특수문자 *(\*)
	// 단계구성 문자 *, +, -는 특수문자 0은 일반문자
	
	// 예제 : 단계구성문자가 *일때 
	// 결과 : "\\*+"
	
	if ("0" != strLevelChar)
	{
		strLevelChar = "\\" + strLevelChar;
	}
	
	var strRegExpr = strLevelChar + "+";
	
	return strRegExpr;
}

/**
 * 체크박스 연결검색 조건식을 최종적으로 추가한다.
 * 
 * @strWhere
 * @strCheckWhere
 *
 * @return strWhere
 */
PQConnectionSearch.prototype._makeCompleteWhereClause = function (strWhere, strCheckWhere)
{
	if (0 < strCheckWhere.length)
	{
		if (0 < strWhere.length)
		{
			strWhere += " AND (" + strCheckWhere + ")";
		}
		else
		{
			strWhere = strCheckWhere;
		}
	}
	
	return strWhere;
}

/**
 * 조건식을 추가한다.
 * 
 * @param strWhere			where절
 * @param strFieldName		필드명
 * @param strValue			값
 * @param strItemVarName	항목변수
 * @param strOP				LIKE, =
 * @param strConn			AND, OR
 *
 * @return strWhere
 */
PQConnectionSearch.prototype._concatWhereClause = function (strWhere, strFieldName, strValue, strItemVarName, strOP, strConn)
{
	
	if (0 == strWhere.length)
	{
		strWhere = strFieldName + strOP + "'" + strValue + "'";
	}
	else
	{
		strWhere += strConn + " " + strFieldName + strOP + "'" + strValue + "'";
	}

	return strWhere;
}	


////////////////////////////////////////
// static method
//


PQConnectionSearch.init = function ()
{
	var arConnSearchInfoList = GetConnectionSearchInfoList();
	
	g_objPQConnectionSearch = new PQConnectionSearch(arConnSearchInfoList);
}

PQConnectionSearch.execute = function (strVarName)
{
	if (null != g_objPQConnectionSearch)
	{
		g_objPQConnectionSearch.execute(strVarName);
	}
}

PQConnectionSearch.makeWhereClause = function (strConnectionVarName)
{
	return g_objPQConnectionSearch.makeWhereClause(strConnectionVarName);
}


/**
 * 연산식  관련 Javascript 클래스
 */

/**
 * TODO 조영운
 * 1. 그리기 아톰에 대한 연산식 논리 추가해야합니다.
 * 2. 연산식에 연결되는 아톰을 하나의 리스트로 관리하도록 만들면 좋겠습니다.
 	- 현재는 데이터입력란, 날짜입력란을 따로 관리합니다.
 */
function PQOperation (objPQOPInfo, xnOperation)
{
	this.m_bChange = false;
	this.m_xnOperate = xnOperation;
	this.m_objPQOPInfo = objPQOPInfo;
	this.m_objPQOPCalculate = new PQOPCalculate(objPQOPInfo);
}
	
PQOperation.prototype.setChange = function (bChange)
{
	this.m_bChange = bChange;
}

PQOperation.prototype.isChange = function ()
{
	return this.m_bChange;
}

PQOperation.prototype.getOperateXML = function ()
{
	return this.m_xnOperate;
}

PQOperation.prototype.setOperateXML = function (xnOperate)
{
	this.m_xnOperate = xnOperate;
}

PQOperation.prototype.makeRequest = function (xnRequest)
{
	var xnRequest = XmlLib.selectSingleNode(xnRequest, "Request/Model");
	if (null != xnRequest)
	{
		var xnOperateRequest = XmlLib.createChild(xnRequest, "Operate");
		if (null != xnOperateRequest)
		{
			XmlLib.importChildNode(xnOperateRequest, this.m_xnOperate, true);
		}
	}
}

/**
 * 연산식을 실행한다.
 *
 * @param heAtom 이벤트(onBlur)가 발생한 아톰
 * @param nRowIndex 스크롤이 묶인 행번호
 */
PQOperation.prototype.execute = function (objAtom)
{
	var strVarName = objAtom.getVarName();
	var nRowIndex = objAtom.getRowIndex();
	
	var objPQOPVar = this.m_objPQOPInfo.getOperationVar(strVarName);
	if (null != objPQOPVar)
	{
		this._executePreOperation(objPQOPVar, nRowIndex);
	}
}

/**
 * 스크롤 행 추가시에 실행시키는 연산식입니다.
 * 스크롤에 묶이지 않은 외부 아톰의 연산식만 실행시킨다.
 */
PQOperation.prototype.executeScrollOuterOperation = function (strVarName, nScrollRowIndex)
{
	var objPQOPAtom = this.m_objPQOPInfo.getOperationAtom(strVarName);
	if (null != objPQOPAtom)
	{
		var arTokenList = objPQOPAtom.getTokenList();
		
		for (var i = 0; i < arTokenList.length; i++)
		{
			var objToken = arTokenList[i];
			var nTokenType = objToken.getTokenType();
			var strTokenString = objToken.getTokenString();
		
			var objVar = this.m_objPQOPInfo.getOperationVar(strTokenString);
				
			if (null != objVar && objVar.isNormalAtom())
			{
				this._executeOperation(objPQOPAtom, nScrollRowIndex);
			}
		}
	}
}

/**
 * 스크롤 내부 값이 바뀐 아톰에 대해서만 연산식을 수행합니다.
 */
PQOperation.prototype.executeScrollChangeOperation = function (strVarName, nScrollRowIndex)
{
	var objPQOPVar = this.m_objPQOPInfo.getOperationVar(strVarName);
	if (null != objPQOPVar)
	{
		this._executePreOperation(objPQOPVar, nScrollRowIndex);
	}
}

PQOperation.prototype.getPQOPInfo = function ()
{
	return this.m_objPQOPInfo;
}

/**
 * 연산식 변수 리스트에 연결된 아톰에 대한 연산식을 실행시킨다. 
 *
 * @xnOpVar
 * @nScrollRowIndex
 */
PQOperation.prototype._executePreOperation = function (objPQOPVar, nScrollRowIndex)
{
	var arRefOpAtomList = objPQOPVar.getRefOpAtomList();
	if (null == arRefOpAtomList)
	{
		return;
	}
	
	var nLen = arRefOpAtomList.length;
	for (var i = 0; i < nLen; i=i+1)
	{
		var strRefOpAtom = arRefOpAtomList[i];

		var objPQOPAtom = this.m_objPQOPInfo.getOperationAtom(strRefOpAtom);
		if (null == objPQOPAtom)
		{
			continue;
		}
		
		// OpVar가 스크롤에 묶여 있지 않고
		// OpAtom이 스크롤에 묶여 있을때
		if ((-1 == nScrollRowIndex) && (objPQOPAtom.isBindedAtom()))
		{
			this._executePreScrollOperation(objPQOPAtom);
		}
		else
		{
			this._executeOperation(objPQOPAtom, nScrollRowIndex);
		}
	}
}

PQOperation.prototype._executePreScrollOperation = function (objPQOPAtom)
{
	var strScrollName = objPQOPAtom.getScrollName();
	var objScrollAtom = null;
	if (ContainsScrollAtom())
	{
		objScrollAtom = ScrollAtom.getAtom(strScrollName);
	}
	if (null == objScrollAtom)
	{
		return;
	}
	
	var nLen = objScrollAtom.getOperationRowSize();
	for (var i = 0; i < nLen; i=i+1)
	{
		this._executeOperation(objPQOPAtom, i);
	}
}

/**
 * 아톰에 대한 연산식을 실행시킨다.
 *
 * @xnOpAtom
 * @nScrollRowIndex
 */
PQOperation.prototype._executeOperation = function (objPQOPAtom, nScrollRowIndex)
{
	var arTokenList = objPQOPAtom.getTokenList();
	if (null == arTokenList || 0 == arTokenList.length)
	{
		return;
	}
	
	var strResult = this.m_objPQOPCalculate.execute(arTokenList, nScrollRowIndex);
	
	this._handleResult(objPQOPAtom, strResult, nScrollRowIndex);
	
	var objPQOPVar = this.m_objPQOPInfo.getOperationVar(objPQOPAtom.getVarName())
	if (null != objPQOPVar)
	{
		this._executePreOperation(objPQOPVar, nScrollRowIndex);
	}
}

/**
 * 연산식 결과를 반영한다.
 *
 * @xnOpAtom
 * @strResult
 * @nScrollRowIndex
 */
PQOperation.prototype._handleResult = function (objPQOPAtom, strResult, nScrollRowIndex)
{
	var objAtom = this.getAtom(objPQOPAtom.getVarName(), nScrollRowIndex);
	
	if (null != objAtom)
	{
		objAtom.setValue(strResult, Define.OCCUR_CHANGE_FLAG);
	}
}

PQOperation.prototype.getAtom = function (strVarName, nScrollRowIndex)
{
	var objPQOPAtom = this.m_objPQOPInfo.getOperationAtom(strVarName);
	var objAtom = null;
	
	if (objPQOPAtom.isNormalAtom())
	{
		/**
		 * 연산식에 연결될 수 있는 아톰은 그리기, 데이터입력란, 날짜입력란이지만
		 * 그리기 아톰의 버그로 데이터입력란, 날짜입력란만 구현
		 */
		if (ContainsInputDataAtom())
		{
			objAtom = InputDataAtom.getAtom(strVarName);
		}
		if (null == objAtom && ContainsInputTimeAtom())
		{
			objAtom = InputTimeAtom.getAtom(strVarName);
		}
		if (null == objAtom && ContainsRectangleAtom())
		{
			objAtom = RectangleAtom.getAtom(strVarName);
		}
		
	}
	else if (objPQOPAtom.isBindedAtom()
		&& -1 != nScrollRowIndex // nScrollRowIndex가 -1이면 스크롤 아톰의 외부키일 경우이다. 결과를 반영하지 않는다.
		&& ContainsScrollAtom())
	{
		var objScrollAtom = ScrollAtom.getAtom(objPQOPAtom.getScrollName());
		
		if (null != objScrollAtom)
		{
			objAtom = objScrollAtom.getBindedAtom(strVarName, nScrollRowIndex);
		}
	}
	
	return objAtom;
}

PQOperation.prototype.makeOperation = function (strOperate, strVarName)
{
	var objCompiler = new PQOPCompiler();
	var alOpToken = objCompiler.doCompile(strOperate);
	
	this.m_objPQOPInfo.makeVarListToAtom(alOpToken, strVarName);
	this.m_objPQOPInfo.makeAtomListToVar(alOpToken, strVarName);
}

PQOperation.init = function ()
{
	var xnOperDoc = PQService.getOperationInfo();
	if (null == xnOperDoc)
	{
		return;
	}
	
	var xnOperation = xnOperDoc.documentElement;
	if (null == xnOperation)
	{
		return;
	}
	
	var objPQOPInfo = new PQOPInfoManager().createPQOPInfo(xnOperation);
	
	g_objPQOperation = new PQOperation(objPQOPInfo, xnOperation);
}

PQOperation.makeRequest = function (xnRequest)
{
	return g_objPQOperation.makeRequest(xnRequest);
}

PQOperation.change = function (xnOperate)
{
	if (null != xnOperate)
	{
		var objPQOPInfo = new PQOPInfoManager().createPQOPInfo(xnOperate);	
		g_objPQOperation = new PQOperation(objPQOPInfo);
		
		g_objPQOperation.setChange(true);
		g_objPQOperation.setOperateXML(xnOperate);
	}
}

PQOperation.isChange = function ()
{
	return g_objPQOperation.isChange()
}

PQOperation.execute = function (objAtom)
{
	if (null != g_objPQOperation)
	{
		g_objPQOperation.execute(objAtom);
	}
}

PQOperation.executeScrollOperation = function (strVarName, nScrollRowIndex)
{
	if (null != g_objPQOperation)
	{
		g_objPQOperation.executeScrollOperation(strVarName, nScrollRowIndex);
	}
}

PQOperation.executeScrollChangeOperation = function (strVarName, nScrollRowIndex)
{
	if (null != g_objPQOperation)
	{
		g_objPQOperation.executeScrollChangeOperation(strVarName, nScrollRowIndex);
	}
}

PQOperation.executeScrollOuterOperation = function (strVarName, nScrollRowIndex)
{
	if (null != g_objPQOperation)
	{
		g_objPQOperation.executeScrollOuterOperation(strVarName, nScrollRowIndex);
	}
}

PQOperation.getAtom = function (strVarName, nScrollRowIndex)
{
	if (null != g_objPQOperation)
	{
		return g_objPQOperation.getAtom(strVarName, nScrollRowIndex);
	}
	
	return null;
}

PQOperation.getRefOpAtomList = function (strVarName)
{
	if (null != g_objPQOperation)
	{
		var objOperationVar = g_objPQOperation.getPQOPInfo().getOperationVar(strVarName);
		return (null == objOperationVar) ? new Array() : objOperationVar.getRefOpAtomList();
	}
	
	return new Array();
}

//----------------------------------------------------------------------------
//----------------------------------------------------------------------------

function PQOPCompiler ()
{
	////////////////////////////////////////
	// private member
	
	this.m_strOperationSource = "";			// 연산식 문자열
	this.m_nSourcePosition = 0;				// 현재 가리키고 있는 연산식 문자열의 위치
	this.m_objCurrentToken = null;			// PQOPToken Object
	this.m_arTokenList = new Array();		// Token List
	this.m_arOperatorList = new Array();		// 연산식 Token List
}	
	
////////////////////////////////////////
// public method

PQOPCompiler.prototype.doCompile = function (strOperation)
{
	return this._doPreParsing(strOperation);
}


////////////////////////////////////////
// private method

PQOPCompiler.prototype._doPreParsing = function (strOperation)
{
	if (0 == strOperation.length)
		return;
	
	if (null == this.m_objCurrentToken)
		this.m_objCurrentToken = new PQOPToken ("", -1);
	
	this.m_arTokenList.length = 0;
	this.m_nSourcePosition = 0;
	this.m_strOperationSource = strOperation;
	
	var strToken = this._getChar(false);
	
	var bError = false;
	switch (strToken)
	{
		case "@":	// Function Indicator
			strToken = this._getChar(false);
			
			switch (strToken)
			{
				case "A": //@A 평균
					this._doExFuncParsing(PQOPDefine.TK_STR_AVERAGE);
					break;
				case "I": //@I : 조건(if)
					this._doIfFuncParsing();
					break;
				case "D":
					strToken = this._getChar(true);
					
					switch (strToken)
					{
						case "A": //@DA 날짜간 차 (Date Term) 
							this._doDAFuncParsing();
							break;
						case "T": //@DT: 날짜더함 (Date Amount)
							this._doDTFuncParsing();
							break;
						default:
							bError = true;
							break;
					}
					break;
				case "M":
					strToken = this._getChar(true);
					
					switch (strToken)
					{
						case "X": //@MX 최대값
							this._doExFuncParsing(PQOPDefine.TK_STR_MAXIMUM); 
							break;
						case "N": //@MN 최소값
							this._doExFuncParsing(PQOPDefine.TK_STR_MINIMUM); 
							break;
						default:
							bError = true;
							break;
					}
					break;
				case "R":
					strToken = this._getChar(true);
					
					switch (strToken)
					{
						case "D": //@RD: 반올림 (Round)
							this._doRDFuncParsing();
							break;
						default:
							bError = true;
							break;
					}
					break;
				case "S": //@S: 합계(SUM)
					this._doExFuncParsing(PQOPDefine.TK_STR_SUM);
					break;
				case "평": //@평균값
				{
					strToken = this._getChar(true);
					switch (strToken)
					{
						case "균":
						{
							strToken = this._getChar(true);
							switch (strToken)
							{
								case "값":
								{
									this._doExFuncParsing(PQOPDefine.TK_STR_AVERAGE);
									break;
								}
								default:
								{
									bError = true;
									break;
								}
							}
							break;
						}
						default:
						{
							bError = true;
							break;
						}
					}
					break;
				}
				case "합": //@합계
				{
					strToken = this._getChar(true);
					switch (strToken)
					{
						case "계":
						{								
							this._doExFuncParsing(PQOPDefine.TK_STR_SUM);
							break;								
						}
						default:
						{
							bError = true;
							break;
						}
					}
					break;
				}
				case "최": 
				{
					strToken = this._getChar(true);
					switch (strToken)
					{
						case "대":
						{
							strToken = this._getChar(true);
							switch (strToken)
							{
								case "값": //@최대값
								{
									this._doExFuncParsing(PQOPDefine.TK_STR_MAXIMUM); 
									break;
								}
								default:
								{
									bError = true;
									break;
								}
							}
							break;
						}
						case "소":
						{
							strToken = this._getChar(true);
							switch (strToken)
							{
								case "값": //@최소값
								{
									this._doExFuncParsing(PQOPDefine.TK_STR_MINIMUM); 
									break;
								}
								default:
								{
									bError = true;
									break;
								}
							}
							break;
						}
						default:
						{
							bError = true;
							break;
						}
					}
					break;
				}
				case "소": //@소수처리
				{
					strToken = this._getChar(true);
					switch (strToken)
					{
						case "수":
						{
							strToken = this._getChar(true);
							switch (strToken)
							{
								case "처":
								{
									strToken = this._getChar(true);
									switch (strToken)
									{
										case "리":
										{
											this._doRDFuncParsing();
											break;
										}
										default:
										{
											bError = true;
											break;
										}
									}
									break;
								}
								default:
								{
									bError = true;
									break;
								}
							}
							break;
						}
						default:
						{
							bError = true;
							break;
						}
					}
					break;
				}
				case "날": //@날짜계산
				{
					strToken = this._getChar(true);
					switch (strToken)
					{
						case "짜":
						{
							strToken = this._getChar(true);
							switch (strToken)
							{
								case "계":
								{
									strToken = this._getChar(true);
									switch (strToken)
									{
										case "산":
										{
											this._doDAFuncParsing();
											break;
										}
										default:
										{
											bError = true;
											break;
										}
									}
									break;
								}
								default:
								{
									bError = true;
									break;
								}
							}
							break;
						}
						default:
						{
							bError = true;
							break;
						}
					}
					break;
				}
				case "기": //@기간계산
				{
					strToken = this._getChar(true);
					switch (strToken)
					{
						case "간":
						{
							strToken = this._getChar(true);
							switch (strToken)
							{
								case "계":
								{
									strToken = this._getChar(true);
									switch (strToken)
									{
										case "산":
										{
											this._doDTFuncParsing();
											break;
										}
										default:
										{
											bError = true;
											break;
										}
									}
									break;
								}
								default:
								{
									bError = true;
									break;
								}
							}
							break;
						}
						default:
						{
							bError = true;
							break;
						}
					}
					break;
				}
				case "만": //@만일
				{
					strToken = this._getChar(true);
					switch (strToken)
					{
						case "일":
						{
							this._doIfFuncParsing();
							break;
						}
						default:
						{
							bError = true;
							break;
						}
					}
					break;
				}
				default:
					bError = true;
					break;
			}
			break;
		case "-":
		case "(":
		case "{":
		case "#":
		default: 
			this.m_nSourcePosition = 0;
			this.m_strOperationSource = Utils.replace(this.m_strOperationSource, "{", "("); // {} 대응
			this.m_strOperationSource = Utils.replace(this.m_strOperationSource, "}", ")");
			this._getNextToken(true);
			this._doParsing();
			break;
	}
	
	var arTokenList = new Array();
	
	if (false == bError)
	{
		var nLen = this.m_arTokenList.length;
		for (var i = 0; i < nLen; i=i+1)
			arTokenList.push(this.m_arTokenList[i]);
	}

	this.m_arTokenList.length = 0;

	if (null != this.m_objCurrentToken)
		this.m_objCurrentToken = null;

	return arTokenList;
}


// PQ 연산식 함수 파싱 ////////////////////

// 평균, 헙계, 최대, 최소 함수 파싱
PQOPCompiler.prototype._doExFuncParsing = function (strFuncToken)
{
	this.m_arTokenList.length = 0;
	var strTmpOperation = this.m_strOperationSource;
	var nStartPos = strTmpOperation.indexOf("(");
	var nEndPos = strTmpOperation.indexOf(")");

	if (-1 != nStartPos && -1 != nEndPos)
	{
		strTmpOperation = strTmpOperation.substring(nStartPos + 1, nEndPos);
		strTmpOperation = Utils.trim(strTmpOperation);
		
		var objToken = null;
		
		if (0 < strTmpOperation.length)
		{
			objToken = new PQOPToken(strTmpOperation, PQOPDefine.TK_TYPE_ID); //디폴트로 우선 TK_TYPE_WORD 
			this.m_arTokenList.push(objToken);
	
			objToken = new PQOPToken(strFuncToken, PQOPDefine.TK_TYPE_FUNCTION); //디폴트로 우선 TK_TYPE_WORD 
			this.m_arTokenList.push(objToken);
		}
	}
}

// If(조건문) 함수 파싱
PQOPCompiler.prototype._doIfFuncParsing = function ()
{
	this.m_arTokenList.length = 0;
	var strTmpOperation = this.m_strOperationSource;
	var nStartPos = strTmpOperation.indexOf("(");
	var nEndPos = strTmpOperation.indexOf(")");
	
	if (-1 != nStartPos && -1 != nEndPos)
	{
		strTmpOperation = strTmpOperation.substring(nStartPos + 1, nEndPos);
		var arBuffer = strTmpOperation.split(PQOPDefine.TK_STR_COMMA);
		
		if (3 == arBuffer.length)
		{
			// 1. 조건 파싱
			this.m_strOperationSource = arBuffer[0];
			this.m_strOperationSource = Utils.trim(this.m_strOperationSource);
			this.m_nSourcePosition = 0;
	
			this.m_strOperationSource = Utils.replace(this.m_strOperationSource, "{", "("); // {} 대응
			this.m_strOperationSource = Utils.replace(this.m_strOperationSource, "}", ")");

			this._getNextToken(true);
			this._doParsing();
			var objToken = null;

			for (var i = 1; i <= 2; i=i+1)
			{
				// 2. 참값,  거짓값 파싱
				var strValue = arBuffer[i];
				strValue = Utils.trim(strValue);
				var nKind = PQOPDefine.TK_TYPE_WORD;

				if (0 < strValue.length)
				{
					var chFirst = strValue.charAt(0);
					
					var strUpper = strValue.toUpperCase();
					if (strUpper != PQOPDefine.TK_STR_AND && strUpper != PQOPDefine.TK_STR_OR &&
							strUpper != PQOPDefine.TK_STR_TRUE && strUpper != PQOPDefine.TK_STR_FALSE &&
							strUpper != PQOPDefine.TK_STR_AND &&
							false == Utils.isDigit(chFirst) && '\'' != chFirst &&
							-1 == PQOPDefine.SEPARATOR1.indexOf(chFirst))
					{
						nKind = PQOPDefine.TK_TYPE_ID;
					}
					else if (strUpper == PQOPDefine.TK_STR_TRUE || strUpper == PQOPDefine.TK_STR_FALSE)
					{
						nKind = PQOPDefine.TK_TYPE_KEYWORD;
					}
					
					if ('\'' == strValue.charAt(0))
					{
						strValue = strValue.substring(1);
						if (0 < strValue.length && '\'' == strValue.charAt(strValue.length - 1))
						{
							strValue = strValue.substring(0, strValue.length - 1);
						}
					}
				}
				
				objToken = new PQOPToken(strValue, nKind); //디폴트로 우선 TK_TYPE_WORD 
				this.m_arTokenList.push(objToken);
			}

			// 3. 함수정보
			objToken = new PQOPToken(PQOPDefine.TK_STR_IF, PQOPDefine.TK_TYPE_FUNCTION);
			this.m_arTokenList.push(objToken);
		}
	}
}

// Date Amount(날짜 계산) 함수 파싱
PQOPCompiler.prototype._doDAFuncParsing = function ()
{
	this.m_arTokenList.length = 0;
	var strTmpOperation = this.m_strOperationSource;
	var nStartPos = strTmpOperation.indexOf("(");
	var nEndPos = strTmpOperation.indexOf(")");
	
	if (-1 != nStartPos && -1 != nEndPos)
	{
		strTmpOperation = strTmpOperation.substring(nStartPos + 1, nEndPos);
		var arBuffer = strTmpOperation.split(PQOPDefine.TK_STR_COMMA);
		
		if (2 == arBuffer.length)
		{
			var objToken = null;

			for (var i = 0; i < 2; i=i+1)
			{
				var strValue = arBuffer[i];
				strValue = Utils.trim(strValue);
				var nKind = PQOPDefine.TK_TYPE_ID; 

				if (0 == i)
				{
					if (0 == strValue.length)
					{
						break; // 에러처리~!!!!
					}
				}
				else
				{
					if (0 == strValue.length || Utils.isDigit(strValue.charAt(0)))
					{
						nKind = PQOPDefine.TK_TYPE_DIGIT;
					}
				}
				
				objToken = new PQOPToken(strValue, nKind); 
				this.m_arTokenList.push(objToken);
			}

			// 함수정보
			objToken = new PQOPToken(PQOPDefine.TK_STR_DATEAMOUNT, PQOPDefine.TK_TYPE_FUNCTION);
			this.m_arTokenList.push(objToken);
		}
	}
}

// Date Term(날짜사이의 간격) 함수 파싱
PQOPCompiler.prototype._doDTFuncParsing = function ()
{
	this.m_arTokenList.length = 0;
	var strTmpOperation = this.m_strOperationSource;
	var nStartPos = strTmpOperation.indexOf("(");
	var nEndPos = strTmpOperation.indexOf(")");

	if (-1 != nStartPos && -1 != nEndPos)
	{
		strTmpOperation = strTmpOperation.substring(nStartPos + 1, nEndPos);
		var arBuffer = strTmpOperation.split(PQOPDefine.TK_STR_COMMA);

		if (2 == arBuffer.length)
		{
			var objToken = null;
			
			for (var i = 0; i < 2; i=i+1)
			{
				var strValue = arBuffer[i];
				strValue = Utils.trim(strValue);
				var nKind = PQOPDefine.TK_TYPE_ID;

				if (0 == strValue.length)
				{
					break;// 에러처리~!!!!
				}

				objToken = new PQOPToken(strValue, nKind); 
				this.m_arTokenList.push(objToken);
			}

			// 함수정보
			objToken = new PQOPToken(PQOPDefine.TK_STR_DATETERM, PQOPDefine.TK_TYPE_FUNCTION);
			this.m_arTokenList.push(objToken);
		}
	}
}

// Round(반올림) 함수 파싱
PQOPCompiler.prototype._doRDFuncParsing = function ()
{
	this.m_arTokenList.length = 0;
	var strTmpOperation = this.m_strOperationSource;
	var nStartPos = strTmpOperation.indexOf("(");
	var nEndPos = strTmpOperation.indexOf(")");

	if (-1 != nStartPos && -1 != nEndPos)
	{
		strTmpOperation = strTmpOperation.substring(nStartPos + 1, nEndPos);
		var arBuffer = strTmpOperation.split(PQOPDefine.TK_STR_COMMA);
		
		if (3 == arBuffer.length)
		{
			var objToken = null;
			
			for (var i = 0; i < 3; i=i+1)
			{
				var strValue = arBuffer[i];
				strValue = Utils.trim(strValue);
				var nKind = PQOPDefine.TK_TYPE_WORD;
				
				if (0 == i)
				{
					if (0 < strValue.length)
					{
						nKind = PQOPDefine.TK_TYPE_ID;
					}
					else
					{
						break; // 에러처리~!!!!
					}
				}
				
				objToken = new PQOPToken(strValue, nKind); 
				this.m_arTokenList.push(objToken);
			}
			
			// 함수정보
			objToken = new PQOPToken(PQOPDefine.TK_STR_ROUND, PQOPDefine.TK_TYPE_FUNCTION);
			this.m_arTokenList.push(objToken);
		}
	}
}


// PQ 연산식 파싱 ////////////////////

PQOPCompiler.prototype._doParsing = function ()
{
	this.m_arTokenList.length = 0;

	this._doExpressionParsing();
	
	var nLen = this.m_arOperatorList.length;
	while (0 < nLen)
	{
		var objToken = this.m_arOperatorList[nLen - 1];
		var strOperator = objToken.getTokenString();
		
		if (PQOPDefine.TK_STR_L_PAREN != strOperator)
		{
			this._insertToken(objToken);
		}

		this.m_arOperatorList.splice(nLen - 1, 1);
		
		nLen = this.m_arOperatorList.length;
	}
	
	return true;
}

PQOPCompiler.prototype._doExpressionParsing = function ()
{
	var bResult = this._doLogicalExprParsing();

	var objToken = this._getNextToken(false);
	var strTokenString = objToken.getTokenString();
	var nTokenType = objToken.getTokenType();

	if (PQOPDefine.TK_TYPE_OPERATOR == nTokenType&& 
		(PQOPDefine.equals(PQOPDefine.TK_STR_AND, strTokenString) || 
			PQOPDefine.equals(PQOPDefine.TK_STR_OR, strTokenString)))
	{	
		var nLen = this.m_arOperatorList.length;
		while (0 < nLen)
		{
			var objTmpToken = this.m_arOperatorList[nLen - 1];
			var strTmpTokenString = objTmpToken.getTokenString();

			if (PQOPDefine.equals(PQOPDefine.TK_STR_L_PAREN, strTmpTokenString))
			{
				break;
			}

			this.m_arOperatorList.length = 0;
			this._insertToken(objTmpToken);
			
			nLen = this.m_arOperatorList.length;
		}
		
		var objNewToken = new PQOPToken(strTokenString, nTokenType);
		this.m_arOperatorList.push(objNewToken);

		this._getNextToken(true);
		
		bResult = this._doExpressionParsing();
	}

	return bResult;
}

PQOPCompiler.prototype._doLogicalExprParsing = function ()
{
	var bResult = this._doCompareExprParsing();

	var objToken = this._getNextToken(false);
	var strTokenString = objToken.getTokenString();
	var nTokenType = objToken.getTokenType();

	if (PQOPDefine.TK_TYPE_OPERATOR == nTokenType && 
		(PQOPDefine.equals(PQOPDefine.TK_STR_EQUAL, strTokenString) || 
			PQOPDefine.equals(PQOPDefine.TK_STR_NOTEQUAL1, strTokenString) ||
			PQOPDefine.equals(PQOPDefine.TK_STR_GREATERTHAN, strTokenString) || 
			PQOPDefine.equals(PQOPDefine.TK_STR_GREATEREQUAL1, strTokenString) ||
			PQOPDefine.equals(PQOPDefine.TK_STR_LESSTHAN, strTokenString) || 
			PQOPDefine.equals(PQOPDefine.TK_STR_LESSEQUAL1, strTokenString)))
	{	
		var nLen = this.m_arOperatorList.length;
		while (0 < nLen)
		{
			var objTmpToken = this.m_arOperatorList[nLen - 1];
			var strTmpTokenString = objTmpToken.getTokenString();

			if (PQOPDefine.equals(PQOPDefine.TK_STR_EQUAL, strTmpTokenString) ||
				PQOPDefine.equals(PQOPDefine.TK_STR_NOTEQUAL1, strTmpTokenString) ||
				PQOPDefine.equals(PQOPDefine.TK_STR_GREATERTHAN, strTmpTokenString) ||
				PQOPDefine.equals(PQOPDefine.TK_STR_GREATEREQUAL1, strTmpTokenString) ||
				PQOPDefine.equals(PQOPDefine.TK_STR_LESSTHAN, strTmpTokenString) ||
				PQOPDefine.equals(PQOPDefine.TK_STR_LESSEQUAL1, strTmpTokenString) ||
				PQOPDefine.equals(PQOPDefine.TK_STR_PLUS, strTmpTokenString) ||
				PQOPDefine.equals(PQOPDefine.TK_STR_MINUS, strTmpTokenString) ||
				PQOPDefine.equals(PQOPDefine.TK_STR_MULTIPLY, strTmpTokenString) ||
				PQOPDefine.equals(PQOPDefine.TK_STR_DIVIDE, strTmpTokenString) ||
				PQOPDefine.equals(PQOPDefine.TK_STR_PERCENT, strTmpTokenString) ||
				PQOPDefine.equals(PQOPDefine.TK_STR_CONCATENATE, strTmpTokenString))
			{
				this.m_arOperatorList.splice(nLen - 1, 1);
				
				this._insertToken(objTmpToken);
			}
			else
			{
				break;
			}
			
			nLen = this.m_arOperatorList.lingth;
		}
		
		var objNewToken = new PQOPToken(strTokenString, nTokenType);
		this.m_arOperatorList.push(objNewToken);

		this._getNextToken (true);
		bResult = this._doCompareExprParsing();
	}

	return bResult;
}

// BNF ==> <비교식> := <산술식> { <Add 연산자> <산술식> } | <산술식>
PQOPCompiler.prototype._doCompareExprParsing = function ()
{
	var bResult = this._doArithmeticExprParsing();
	var objToken = this._getNextToken(false);
	var strTokenString = objToken.getTokenString();
	var nTokenType = objToken.getTokenType();

	if (PQOPDefine.TK_TYPE_OPERATOR == nTokenType&& 
		(PQOPDefine.equals(PQOPDefine.TK_STR_PLUS, strTokenString) || 
			PQOPDefine.equals(PQOPDefine.TK_STR_MINUS, strTokenString)))
	{	
		var nLen = this.m_arOperatorList.length;
		while (0 < nLen)
		{
			var objTmpToken = this.m_arOperatorList[nLen - 1];
			var strTmpTokenString = objTmpToken.getTokenString();

			if (PQOPDefine.equals(PQOPDefine.TK_STR_PLUS, strTmpTokenString) || 
				PQOPDefine.equals(PQOPDefine.TK_STR_MINUS, strTmpTokenString) || 
				PQOPDefine.equals(PQOPDefine.TK_STR_MULTIPLY, strTmpTokenString) || 
				PQOPDefine.equals(PQOPDefine.TK_STR_DIVIDE, strTmpTokenString) || 
				PQOPDefine.equals(PQOPDefine.TK_STR_PERCENT, strTmpTokenString) ||
				PQOPDefine.equals(PQOPDefine.TK_STR_CONCATENATE, strTmpTokenString))
			{
				this.m_arOperatorList.splice(nLen - 1, 1);
				this._insertToken(objTmpToken);
			}
			else
			{
				break;
			}
			
			nLen = this.m_arOperatorList.length;
		}
		
		var objNewToken = new PQOPToken(strTokenString, nTokenType);
		this.m_arOperatorList.push(objNewToken);

		this._getNextToken (true);
		bResult = this._doCompareExprParsing();
	}

	return bResult;
}

// BNF ==> <산술식> := <Term> { <Mul 연산자> <Term> } | <Term>
PQOPCompiler.prototype._doArithmeticExprParsing = function ()
{
	var bResult = this._doConcatenateExprParsing();
	var objToken = this._getNextToken(false);
	var strTokenString = objToken.getTokenString();
	var nTokenType = objToken.getTokenType();

	if (PQOPDefine.TK_TYPE_OPERATOR == nTokenType && 
		(PQOPDefine.equals(PQOPDefine.TK_STR_MULTIPLY, strTokenString) || 
			PQOPDefine.equals(PQOPDefine.TK_STR_DIVIDE, strTokenString) || 
			PQOPDefine.equals(PQOPDefine.TK_STR_PERCENT, strTokenString)))
	{
		var nLen = this.m_arOperatorList.length;
		while (0 < nLen)
		{
			var objTmpToken = this.m_arOperatorList[nLen - 1];
			var strTmpTokenString = objTmpToken.getTokenString();

			if (PQOPDefine.equals(PQOPDefine.TK_STR_MULTIPLY, strTmpTokenString) || 
				PQOPDefine.equals(PQOPDefine.TK_STR_DIVIDE, strTmpTokenString) || 
				PQOPDefine.equals(PQOPDefine.TK_STR_PERCENT, strTmpTokenString) ||
				PQOPDefine.equals(PQOPDefine.TK_STR_CONCATENATE, strTmpTokenString))
			{
				this.m_arOperatorList.splice(nLen - 1, 1);
				this._insertToken(objTmpToken);
			}
			else
			{
				break;
			}
			
			nLen = this.m_arOperatorList.length;
		}
		
		var objNewToken = new PQOPToken(strTokenString, nTokenType);
		this.m_arOperatorList.push(objNewToken);

		this._getNextToken(true);
		bResult = this._doArithmeticExprParsing();
	}

	return bResult;
}

// BNF ==> | 처리
PQOPCompiler.prototype._doConcatenateExprParsing = function ()
{
	var bResult = this._doFactorExprParsing();
	var objToken = this._getNextToken(false);
	var strTokenString = objToken.getTokenString();
	var nTokenType = objToken.getTokenType();

	if (PQOPDefine.TK_TYPE_OPERATOR == nTokenType &&
		PQOPDefine.equals(PQOPDefine.TK_STR_CONCATENATE, strTokenString))
	{
		var nLen = this.m_arOperatorList.length;
		while (0 < nLen)
		{
			var objTmpToken = this.m_arOperatorList[nLen - 1];
			var strTmpTokenString = objTmpToken.getTokenString();

			if (PQOPDefine.equals(PQOPDefine.TK_STR_CONCATENATE, strTmpTokenString))
			{
				this.m_arOperatorList.splice(nLen - 1, 1);
				this._insertToken(objTmpToken);
			}
			else
			{
				break;
			}
			
			nLen = this.m_arOperatorList.length;
		}
		
		var objNewToken = new PQOPToken(strTokenString, nTokenType);
		this.m_arOperatorList.push(objNewToken);

		this._getNextToken(true);
		bResult = this._doConcatenateExprParsing();
	}

	return bResult;
}

// BNF ==>  <Factor> :=	ID
//					  | <상수>
//					  | ( <식> )
//                    | + | - | Not <Factor>
PQOPCompiler.prototype._doFactorExprParsing = function ()
{
	var bResult = true;

	var objToken = this._getNextToken(false);
	var strTokenString = objToken.getTokenString();
	var nTokenType = objToken.getTokenType();

	// <ID> 
	if (PQOPDefine.TK_TYPE_ID == nTokenType)
	{
		var objIDToken = new PQOPToken(strTokenString, nTokenType);
		bResult = this._insertToken(objIDToken);
	}
	// <상수>
	else if (PQOPDefine.TK_TYPE_DIGIT == nTokenType)
	{	
		var objDigitToken = new PQOPToken(strTokenString, PQOPDefine.TK_TYPE_DIGIT);
		bResult = this._insertToken(objDigitToken);
	}
	// - 
	else if (PQOPDefine.TK_TYPE_OPERATOR == nTokenType &&
			PQOPDefine.equals(PQOPDefine.TK_STR_MINUS, strTokenString))
	{
		var strOperator = strTokenString + "_U"; 

		this._getNextToken(true);
		this._doFactorExprParsing();

		var objOpToken = new PQOPToken(strOperator, nTokenType);
		bResult = this._insertToken(objOpToken);
		
		return true;
	}
	// + 
	else if (PQOPDefine.TK_TYPE_OPERATOR == nTokenType &&
			PQOPDefine.equals(PQOPDefine.TK_STR_PLUS, strTokenString))
	{
		this._getNextToken(true);
		
		return bResult;
	}
	else if (PQOPDefine.TK_TYPE_OPERATOR == nTokenType &&
			PQOPDefine.equals(PQOPDefine.TK_STR_L_PAREN, strTokenString))
	{
		var objOpToken = new PQOPToken(strTokenString, nTokenType);
		this.m_arOperatorList.push(objOpToken);

		this._getNextToken(true);		
		this._doExpressionParsing();

		objToken = this._getNextToken(false);
		nTokenType = objToken.getTokenType();
		strTokenString = objToken.getTokenString();

		if (PQOPDefine.TK_TYPE_OPERATOR == nTokenType &&
			PQOPDefine.equals(PQOPDefine.TK_STR_R_PAREN, strTokenString))
		{
			var nLen = this.m_arOperatorList.length;
			while (0 < nLen)
			{
				var objTmpOpToken = this.m_arOperatorList[nLen - 1];
				this.m_arOperatorList.splice(nLen - 1, 1);
				var strTmpOpTokenString = objTmpOpToken.getTokenString();

				if (PQOPDefine.equals(PQOPDefine.TK_STR_L_PAREN, strTmpOpTokenString))
				{
					objTmpOPToken = null;
					break;
				}
				
				this._insertToken (objTmpOpToken);
				
				nLen = this.m_arOperatorList.length;
			}
		}
	}
	else
	{
		if (PQOPDefine.TK_TYPE_WORD == nTokenType || 
			PQOPDefine.TK_TYPE_DIGIT == nTokenType)	// 상위 if 조건식에 TK_TYPE_DIGIT가 있는데 여기에 올까???
		{
			var objTmpToken = new PQOPToken(strTokenString, nTokenType);
			bResult = this._insertToken(objTmpToken);
		}
	}

	this._getNextToken (true);

	return bResult;
}


// common method ////////////////////

PQOPCompiler.prototype._getChar = function (bBlank)
{
	var nLen = this.m_strOperationSource.length;
	if (nLen <= this.m_nSourcePosition)
		return PQOPDefine.EOF;
	
	var strCh = this.m_strOperationSource.charAt(this.m_nSourcePosition++);
	//var strCh2;
	
	//if (this.m_nSourcePosition < nLen)
	//	strCh2 = this.m_strOperationSource.charAt(this.m_nSourcePosition);
	
	if (!bBlank)
	{
		while (" " == strCh)
		{
			strCh = this.m_strOperationSource.charAt(this.m_nSourcePosition++);
			if (nLen <= this.m_nSourcePosition) 
				return PQOPDefine.EOF;
		}
		
		return strCh;
	}

	if (PQOPDefine.EOS == strCh)
		return PQOPDefine.EOF;

	return strCh;
}

PQOPCompiler.prototype._insertToken = function (objToken)
{
	this.m_arTokenList.push(objToken);
	
	return true;
}

PQOPCompiler.prototype._getNextToken = function (bSeperate)
{
	if (bSeperate)
		return this._separateNextToken();

	var objToken = this._getCurrentToken();

	return objToken;
}

PQOPCompiler.prototype._searchToken = function (strOperation, strToken)
{
	var nPosition = strOperation.indexOf(strToken);

	if (-1 < nPosition)	
		return nPosition;

	return PQOPDefine.NOTFOUND;
}

PQOPCompiler.prototype._getCurrentToken = function ()
{
	return this.m_objCurrentToken;
}

PQOPCompiler.prototype._separateNextToken = function ()
{
	var strCh = PQOPDefine.EOF;
	strCh = this._getChar(true);

	// Token 분리 / 분석
	while (PQOPDefine.EOF != strCh)
	{
		// blank or tab이면
		if (" " == strCh || "\t" == strCh)
		{
			strCh = this._getChar(true);
			continue;
		}

		// and, or, true, false 키워드 (한글 키워드 없음!)
		if (false != this._isAlpha(strCh))
			return this._getKeywordToken(strCh);
		// 숫자
		else if (false != Utils.isDigit(strCh))
			return this._getNumberToken(strCh);
		// 문자
		else if ("\"" == strCh)
			return this._getWordToken();
		// 변수
		else if (PQOPDefine.SEPARATOR1.indexOf(strCh) != -1)
			return this._getOperatorToken(strCh);
		else
			return this._getVariableToken(strCh);
	}
	
	return null;
}

PQOPCompiler.prototype._getKeywordToken = function (strCh)
{
	var strBuf = strCh;
	
	strCh = this._getChar(true);
	while (PQOPDefine.EOF != strCh)
	{
		if (" " == strCh ||
			PQOPDefine.NOTFOUND != this._searchToken(PQOPDefine.SEPARATOR1, strCh))
		{
			this.m_nSourcePosition--;
			break;
		}
		
		strBuf += strCh;
		
		strCh = this._getChar(true);
	}
	
	if (null != this.m_objCurrentToken)
	{
		if (PQOPDefine.equals(PQOPDefine.TK_STR_AND, strBuf) ||
			PQOPDefine.equals(PQOPDefine.TK_STR_OR, strBuf))
		{
			strBuf = strBuf.toUpperCase();
			this.m_objCurrentToken.setTokenString(strBuf);
			this.m_objCurrentToken.setTokenType(PQOPDefine.TK_TYPE_OPERATOR);
		}
		else
		{
			this.m_nSourcePosition -= (strBuf.length);
			strCh = this._getChar(true);
						
			this._getVariableToken(strCh);
		}
	}
	
	return this.m_objCurrentToken;
}

PQOPCompiler.prototype._getNumberToken = function (strCh)
{
	var strBuf = strCh;

	// 숫자로 시작하다가 문자가 나오면 숫자형태로 바뀔때 0 처리
	strCh = this._getChar(true);
	while (PQOPDefine.EOF != strCh)
	{
		if (" " == strCh ||
			PQOPDefine.NOTFOUND != this._searchToken(PQOPDefine.SEPARATOR1, strCh))
		{
			this.m_nSourcePosition--;
			break;
		}
		
		strBuf += strCh;
		
		strCh = this._getChar(true);
	}
	
	if (null != this.m_objCurrentToken)
	{
		this.m_objCurrentToken.setTokenString(strBuf);
			this.m_objCurrentToken.setTokenType(PQOPDefine.TK_TYPE_DIGIT);   		
	}
	
	return this.m_objCurrentToken;
}

PQOPCompiler.prototype._getWordToken = function ()
{
	var chCharacter = this._getChar(true);
	var bEndsWithDoubleQuotation = false;
	var strBuf = "";
	
	while (PQOPDefine.EOF != chCharacter)
	{
		if ("\\" == chCharacter)
		{
			chCharacter = this._getChar(true);
			
			if ("\'" != chCharacter)
				strBuf += "\\";
		}
		else if ("\"" == chCharacter)
		{
			bEndsWithDoubleQuotation = true;
			break;
		}
		
		if (PQOPDefine.EOF == chCharacter)
		{
			break;
		}
		
		strBuf += chCharacter;
		
		chCharacter = this._getChar(true);
	}

	if (this.m_objCurrentToken && bEndsWithDoubleQuotation)
	{
		this.m_objCurrentToken.setTokenString(strBuf);
		this.m_objCurrentToken.setTokenType(PQOPDefine.TK_TYPE_WORD);
	}

	return this.m_objCurrentToken;
}

PQOPCompiler.prototype._getVariableToken = function (strCh)
{
	var nType = PQOPDefine.TK_TYPE_ID; 
	var bError = false;
	var bScroll = false;
	
	var strBuf = strCh;
	
	strCh = this._getChar(true);
	while (PQOPDefine.EOF != strCh)
	{
		var nKindSeparator = -1;
		nKindSeparator = this._searchToken(PQOPDefine.SEPARATOR1, strCh);
		
		if (PQOPDefine.NOTFOUND != nKindSeparator)
		{
			if (false == bScroll && 10 == nKindSeparator)
			{
				bScroll = true;
			}
			else if (false != bScroll)
			{
				if (7 == nKindSeparator)
					bScroll = false;								
				else
				{
					bError = true; 
					break;
				}
			}
			else
			{
				this.m_nSourcePosition--;
				break;
			}
		}
		else if (false != bScroll || PQOPDefine.EOF == strCh || " " == strCh)
		{
			break;
		}
		
		strBuf += strCh;
		
		strCh = this._getChar(true);
	}

	if (null != this.m_objCurrentToken)
	{
		if (false != bError)
		{
			this.m_objCurrentToken.setTokenType(PQOPDefine.TK_TYPE_ERROR);
			
			return this.m_objCurrentToken;
		}

		// 대문자만.. 키워드 소문자 고유변수명 사용할 수 있다.
		if (PQOPDefine.equals(PQOPDefine.TK_STR_TRUE, strBuf) || 
			PQOPDefine.equals(PQOPDefine.TK_STR_FALSE, strBuf))
		{
			nType = PQOPDefine.TK_TYPE_KEYWORD;
		}

		this.m_objCurrentToken.setTokenString(strBuf);
		this.m_objCurrentToken.setTokenType(nType);
	}

	return this.m_objCurrentToken;
}

PQOPCompiler.prototype._getOperatorToken = function (strCh)
{
	var strBuf = strCh;

	switch (strCh)
	{
		case "=":
			strCh = this._getChar(true);
			
			if ("=" == strCh)
			{
				strBuf += strCh;
			}
			else
			{
				this.m_nSourcePosition--;
			}
			break;
		case "<":
			strCh = this._getChar(true);
			
			if ("=" == strCh || ">" == strCh)
			{
				strBuf += strCh;
			}
			else
			{
				this.m_nSourcePosition--;
			}
			break;
		case ">":
			strCh = this._getChar(true);
			
			if ("=" == strCh)
			{
				strBuf += strCh;
			}
			else
			{
				this.m_nSourcePosition--;
			}
			break;
		default:
			break;
	}
	
	if (null != this.m_objCurrentToken)
	{
		this.m_objCurrentToken.setTokenString(strBuf);
		this.m_objCurrentToken.setTokenType(PQOPDefine.TK_TYPE_OPERATOR);   		
	}

	return this.m_objCurrentToken;
}

//////////
// String method

PQOPCompiler.prototype._isAlpha = function (strCh)
{
	if (("a" <= strCh && "z" >= strCh) ||
		("A" <= strCh && "Z" >= strCh))
		return true;

	return false;
}


//----------------------------------------------------------------------------
//----------------------------------------------------------------------------


function PQOPCalculate (objPQOPInfo)
{
	this.m_objPQOPInfo = objPQOPInfo;
}
	
PQOPCalculate.prototype.execute = function (arOriginTokenList, nScrollRowIndex)
{
	if (null == arOriginTokenList)
		return;	// Error

	var arTokenList = new Array();
	
	this._copyToken(arTokenList, arOriginTokenList);
	this._replaceRealAddress(arTokenList);
	this._replaceRealValue(arTokenList, nScrollRowIndex);
	
	var strResult = this._evaluate(arTokenList);
	
	return strResult;
}


////////////////////////////////////////
// 선행 함수

PQOPCalculate.prototype._copyToken = function (arTokenList, arOriginTokenList)
{
	var nLen = arOriginTokenList.length;
	for (var i = 0; i < nLen; i=i+1)
	{
		var objToken = arOriginTokenList[i];
		if (null == objToken)
		{
			break;	// Error
		}
		
		var strTokenString = objToken.getTokenString();
		var nTokenType = objToken.getTokenType();
		
		var objNewToken = new PQOPToken(strTokenString, nTokenType);
		
		arTokenList.push(objNewToken);
	}
}

PQOPCalculate.prototype._replaceRealAddress = function (arTokenList)
{
	var nTokenCnt = arTokenList.length;

	for (var i = 0; i < nTokenCnt; i=i+1)
	{
		var objToken = arTokenList[i];
		if (null == objToken)
		{
			break; //Error
		}

		var strTokenString = objToken.getTokenString();
		var nTokenType = objToken.getTokenType();

		//Skip Replacement
		if (this._isContainColon(strTokenString) && ((i + 1) < nTokenCnt))
		{
			var objNextToken = arTokenList[i + 1];
			
			var strNextTokenString = objNextToken.getTokenString();
			var nNextTokenType = objNextToken.getTokenType();
			
			if (PQOPDefine.TK_TYPE_FUNCTION == nNextTokenType &&
				(PQOPDefine.equals(PQOPDefine.TK_STR_AVERAGE, strNextTokenString) ||
					PQOPDefine.equals(PQOPDefine.TK_STR_SUM, strNextTokenString) || 
					PQOPDefine.equals(PQOPDefine.TK_STR_MAXIMUM, strNextTokenString) ||
					PQOPDefine.equals(PQOPDefine.TK_STR_MINIMUM, strNextTokenString)))
			{
				i=i+1;
				continue;
			}
		}
		
		if (PQOPDefine.TK_TYPE_ID != nTokenType)
			continue;

		var strKey = this._getGridVarName(strTokenString);

		var objPQOPVar = this._getOperationVar(strKey);
		if (null != objPQOPVar)
		{
			var strAddr = strKey;
			
			objToken.setTokenString(strAddr);
			arTokenList[i] = objToken;
		}
	}
}

PQOPCalculate.prototype._replaceRealValue = function (arTokenList, nScrollRowIndex)
{
	var nTokenCnt = arTokenList.length;

	for (var i = 0; i < nTokenCnt; i=i+1)
	{
		var objToken = arTokenList[i];
		if (null == objToken)
			break; //Error

		var strTokenString = objToken.getTokenString();
		var nTokenType = objToken.getTokenType();

		if (i + 1 < nTokenCnt)
		{
			var objNextToken = arTokenList[i + 1];

			var strNextTokenString = objNextToken.getTokenString();
			var nNextTokenType = objNextToken.getTokenType();
			
			if (PQOPDefine.TK_TYPE_FUNCTION == nNextTokenType &&
				(PQOPDefine.equals(PQOPDefine.TK_STR_AVERAGE, strNextTokenString) ||
					PQOPDefine.equals(PQOPDefine.TK_STR_SUM, strNextTokenString) ||
					PQOPDefine.equals(PQOPDefine.TK_STR_MAXIMUM, strNextTokenString) ||
					PQOPDefine.equals(PQOPDefine.TK_STR_MINIMUM, strNextTokenString)))
			{
				i=i+1;
				continue;
			}
		}
		
		if (PQOPDefine.TK_TYPE_ID != nTokenType)
		{
			continue;
		}
		
		var strKey = this._getGridVarName(strTokenString);
		var strSubVar = this._getSubVarName(strTokenString);
		
		var objPQOPVar = this._getOperationVar(strKey);		
		if (null == objPQOPVar)
		{
			continue;
		}
		
		var strData = "";
		if (objPQOPVar.isBindedAtom())
		{
			strData = this._getScrollBindedAtomData(nScrollRowIndex, strSubVar, objPQOPVar.getScrollName());
		}
		else
		{
			strData = this._getNormalAtomData(strKey);
		}

		objToken.setTokenString(strData);
		arTokenList[i] = objToken;
	}
}


///////////////////////////////////////////////////////
// 연산 실행 함수

PQOPCalculate.prototype._evaluate = function (arTokenList)
{
	var arStack = new Array();

	var nLen = arTokenList.length;
	for (var i = 0; i < nLen; i=i+1)
	{
		var objToken = arTokenList[i];
		if (null == objToken)
			return ""; 

		var nTokenType = objToken.getTokenType();
		var strTokenString = objToken.getTokenString();

		if (PQOPDefine.TK_TYPE_OPERATOR == nTokenType || PQOPDefine.TK_TYPE_FUNCTION == nTokenType)
			nTokenType = PQOPToken.getTokenType(strTokenString);

		switch (nTokenType)
		{
			// 산술연산자 처리
			case PQOPDefine.TK_TYPE_PLUS:	
			case PQOPDefine.TK_TYPE_MINUS:	
			case PQOPDefine.TK_TYPE_MULTIPLY:		
			case PQOPDefine.TK_TYPE_DIVIDE:
			case PQOPDefine.TK_TYPE_ASSIGN:
			case PQOPDefine.TK_TYPE_CONCATENATE:
			case PQOPDefine.TK_TYPE_UMINUS:
			{
				var objToken1 = arStack.pop();
				var strTokenString1 = objToken1.getTokenString();
				
				var strTokenString2 = "";
				if (PQOPDefine.TK_TYPE_UMINUS != nTokenType)
				{
					var objToken2 = arStack.pop();
					strTokenString2 = objToken2.getTokenString();
				}

				var strResult = this._funcToArithmetic(strTokenString1, strTokenString2, nTokenType);
				
				var objNewToken = new PQOPToken(strResult, PQOPDefine.TK_TYPE_DIGIT);
				arStack.push(objNewToken);
				
				break;
			}
			
			// 관계연산자 처리
			case PQOPDefine.TK_TYPE_AND:	
			case PQOPDefine.TK_TYPE_OR:
			case PQOPDefine.TK_TYPE_EQUAL:		
			case PQOPDefine.TK_TYPE_NOTEQUAL:		
			case PQOPDefine.TK_TYPE_GREATERTHAN:		
			case PQOPDefine.TK_TYPE_GREATEREQUAL:		
			case PQOPDefine.TK_TYPE_LESSTHAN:		
			case PQOPDefine.TK_TYPE_LESSEQUAL:
			{
				var objToken1 = arStack.pop();
				var strTokenString1 = objToken1.getTokenString();
				var nTokenType1 = objToken1.getTokenType();
				
				var objToken2 = arStack.pop();
				var strTokenString2 = objToken2.getTokenString();
				var nTokenType2 = objToken2.getTokenType();
			
				var strResult = PQOPDefine.TK_STR_TRUE;
				if (!this._funcToRelation(strTokenString1, nTokenType1, strTokenString2, nTokenType2, nTokenType))  
				{
					strResult = PQOPDefine.TK_STR_FALSE;
				}

				var objNewToken = new PQOPToken(strResult, PQOPDefine.TK_TYPE_KEYWORD);
				arStack.push(objNewToken);
				
				break;
			}
			
			case PQOPDefine.TK_TYPE_DIGIT:
			case PQOPDefine.TK_TYPE_ID:
			case PQOPDefine.TK_TYPE_WORD:
			{
				var objNewToken = new PQOPToken(strTokenString, nTokenType);
				arStack.push(objNewToken);
				
				break;
			}
			
			// AVERAGE Function
			// MAXIMUM Function
			// MINIMUM Function
			// SUM Function
			case PQOPDefine.TK_TYPE_AVERAGE:
			case PQOPDefine.TK_TYPE_MAXIMUM:
			case PQOPDefine.TK_TYPE_MINIMUM:
			case PQOPDefine.TK_TYPE_SUM:
			{
				var objToken1 = arStack.pop();
				var strTokenString1 = objToken1.getTokenString();
				
				// var strOperand1 = this._getGridVarName(strTokenString1);
				var strOperand1 = this._getScrollName(strTokenString1);
				var strOperand2 = this._getSubVarName(strTokenString1);
				
				return this._funcToScroll(strOperand1, strOperand2, nTokenType);
			}
			
			// IF Function
			case PQOPDefine.TK_TYPE_IF:
			{
				var objToken1 = arStack.pop();
				var strTokenString1 = objToken1.getTokenString();
				var nTokenType1 = objToken1.getTokenType();
				
				var objToken2 = arStack.pop();
				var strTokenString2 = objToken2.getTokenString();
				var nTokenType2 = objToken2.getTokenType();
				
				var objToken3 = arStack.pop();
				var strTokenString3 = objToken3.getTokenString();
				var nTokenType3 = objToken3.getTokenType();

				var objNewToken = null;
				if (PQOPDefine.TK_TYPE_KEYWORD == nTokenType3 &&
					PQOPDefine.equals(PQOPDefine.TK_STR_TRUE, strTokenString3))
				{
					objNewToken = new PQOPToken(strTokenString2, PQOPDefine.TK_TYPE_WORD);
				}
				else if (PQOPDefine.TK_TYPE_KEYWORD == nTokenType3 && 
					PQOPDefine.equals(PQOPDefine.TK_STR_FALSE, strTokenString3))
				{
					objNewToken = new PQOPToken(strTokenString1, PQOPDefine.TK_TYPE_WORD);
				}
				arStack.push(objNewToken);
				
				break;
			}
			// ROUND Function
			case PQOPDefine.TK_TYPE_ROUND: 	
			{
				var arPostfix = new Array();

				for (var j = 0; j < 4; j=j+1)
				{
					objToken = arTokenList[j];

					if (null == objToken)
						return "";

					arPostfix.push(objToken.getTokenString());
				}
				
				return this._funcToRound(arPostfix[0], arPostfix[1], arPostfix[2]);
			}
			// DATEAMOUNT Function
			case PQOPDefine.TK_TYPE_DATEAMOUNT:
			{
				var arPostfix = new Array();

				for (var j = 0; j < 2; j=j+1)
				{
					objToken = arTokenList[j];

					if (null == objToken)
						return "";

					arPostfix.push(objToken.getTokenString());
				}
				
				return this._funcToDateAmount(arPostfix[0], arPostfix[1]);
			}
			// DATETERM Function 
			case PQOPDefine.TK_TYPE_DATETERM:	
			{
				var arPostfix = new Array();

				for (var j = 0; j < 2; j=j+1)
				{
					objToken = arTokenList[j];

					if (null == objToken)
						return "";

					arPostfix.push(objToken.getTokenString());
				}
				
				return this._funcToDateTerm(arPostfix[0], arPostfix[1]);
			}
			default:
				break;
		} 
	} 

	var objToken = arStack.pop();
	var strTokenString = objToken.getTokenString();

	return strTokenString.toString();
}

// 산술연산자
PQOPCalculate.prototype._funcToArithmetic = function (strOperand1, strOperand2, nOperatorType)
{
	var strResult = "";

	switch (nOperatorType)
	{
		case PQOPDefine.TK_TYPE_PLUS:
			strResult = this._plus(strOperand2, strOperand1);
			break;
		case PQOPDefine.TK_TYPE_MINUS:
			strResult = this._minus(strOperand2, strOperand1);
			break;
		case PQOPDefine.TK_TYPE_UMINUS:
			strResult = this._uminus(strOperand1);
			break;
		case PQOPDefine.TK_TYPE_MULTIPLY:
			strResult = this._multiply(strOperand2, strOperand1);
			break;
		case PQOPDefine.TK_TYPE_DIVIDE:
			strResult = this._divide(strOperand2, strOperand1);
			break;
		case PQOPDefine.TK_TYPE_ASSIGN:
			strResult = this._assign(strOperand2, strOperand1);
			break;
		case PQOPDefine.TK_TYPE_CONCATENATE:
			strResult = this._concatenate(strOperand2, strOperand1);
			break;
		default:
			break;
	}

	return strResult;
}

// 관계연산자
PQOPCalculate.prototype._funcToRelation = function (strOperand1, nType1, strOperand2, nType2, nRelateType)
{
	var bResult = false;
	var dOp1 = 0.0;
	var dOp2 = 0.0;

	if (PQOPDefine.TK_TYPE_DIGIT == nType1 || PQOPDefine.TK_TYPE_DIGIT == nType2)
	{
		dOp1 = (0 < strOperand1.length) ? parseFloat(strOperand1) : 0.0;
		dOp2 = (0 < strOperand2.length) ? parseFloat(strOperand2) : 0.0;
	}
	
	switch (nRelateType)
	{
		case PQOPDefine.TK_TYPE_AND:
		{
			if (PQOPDefine.TK_TYPE_KEYWORD == nType1 && PQOPDefine.TK_TYPE_KEYWORD == nType2)
			{
				bResult = 
					(PQOPDefine.TK_TYPE_TRUE == PQOPToken.getTokenType(strOperand1) &&
					PQOPDefine.TK_TYPE_TRUE == PQOPToken.getTokenType(strOperand2));
			}
			
			break;
		}
		case PQOPDefine.TK_TYPE_OR:
		{
			if (PQOPDefine.TK_TYPE_KEYWORD == nType1 && PQOPDefine.TK_TYPE_KEYWORD == nType2)
			{
				bResult = 
					(PQOPDefine.TK_TYPE_TRUE == PQOPToken.getTokenType(strOperand1) ||
					PQOPDefine.TK_TYPE_TRUE == PQOPToken.getTokenType(strOperand2));
			}
			
			break;
		}
		case PQOPDefine.TK_TYPE_EQUAL:
		{
			if (PQOPDefine.TK_TYPE_ID == nType1 && PQOPDefine.TK_TYPE_ID == nType2)
			{
				if (PQOPDefine.TK_TYPE_DIGIT == PQOPToken.getTokenType(strOperand1))
				{
					nType1 = PQOPDefine.TK_TYPE_DIGIT;
				}
				if (PQOPDefine.TK_TYPE_DIGIT == PQOPToken.getTokenType(strOperand2))
				{
					nType2 = PQOPDefine.TK_TYPE_DIGIT;
				}
			}
		
			if (PQOPDefine.TK_TYPE_DIGIT == nType1 || PQOPDefine.TK_TYPE_DIGIT == nType2)
			{				
				bResult = (dOp1 == dOp2);
			}
			else
			{
				bResult = (strOperand1.toUpperCase() == strOperand2.toUpperCase());
			}
			
			break;
		}
		case PQOPDefine.TK_TYPE_NOTEQUAL:
		{
			if (PQOPDefine.TK_TYPE_ID == nType1 && PQOPDefine.TK_TYPE_ID == nType2)
			{
				if (PQOPDefine.TK_TYPE_DIGIT == PQOPToken.getTokenType(strOperand1))
				{
					nType1 = PQOPDefine.TK_TYPE_DIGIT;
				}
				if (PQOPDefine.TK_TYPE_DIGIT == PQOPToken.getTokenType(strOperand2))
				{
					nType2 = PQOPDefine.TK_TYPE_DIGIT;
				}
			}

			if (PQOPDefine.TK_TYPE_DIGIT == nType1 || PQOPDefine.TK_TYPE_DIGIT == nType2)
			{
				bResult = (dOp1 != dOp2);
			}
			else
			{
				bResult = (strOperand1.toUpperCase() != strOperand2.toUpperCase());
			}
			
			break;
		}
		case PQOPDefine.TK_TYPE_GREATERTHAN:			
			bResult = (dOp2 > dOp1);
			break;
		case PQOPDefine.TK_TYPE_GREATEREQUAL:
			bResult = (dOp2 >= dOp1);
			break;
		case PQOPDefine.TK_TYPE_LESSTHAN:
			bResult = (dOp2 < dOp1);
			break;
		case PQOPDefine.TK_TYPE_LESSEQUAL:
			bResult = (dOp2 <= dOp1);
			break;
		default:
			break;
	}

	return bResult;
}

// 스크롤 관련 함수
PQOPCalculate.prototype._funcToScroll = function (strScrollName, strVarName, nFuncType)
{
	var objPQOPVar = this._getOperationVar(strScrollName);
	if(null == objPQOPVar)
		return "";

	//var bScrollBinded = objPQOPVar.isBindedAtom();
	//if (!bScroll)
	//	return "";

	var dContent = 0;
	var dMaxContent = PQOPDefine.CONTENT_MIN;
	var dMinContent = PQOPDefine.CONTENT_MAX;
	var dSumContent = 0;
	var nContentCnt = 0;

	var nRowCnt = this._getScrollRowCount(strScrollName);
	for (var i = 0; i < nRowCnt; i=i+1)
	{
		var strValue = this._getScrollBindedAtomData(i, strVarName, strScrollName);

		if (0 == strValue.length)
			continue;

		dContent = (0 < strValue.length) ? parseFloat(strValue) : 0.0;
		
		dSumContent += dContent;
		nContentCnt++;
		dMaxContent = (dMaxContent > dContent) ? dMaxContent : dContent;
		dMinContent = (dMinContent < dContent) ? dMinContent : dContent;
	}

	var dAverage = 0;
	var strResult = "";
	switch (nFuncType)
	{
		case PQOPDefine.TK_TYPE_AVERAGE:
		{
			if (0 != nContentCnt)
			{
				dAverage = dSumContent / nContentCnt;
				strResult = new String(dAverage);
			}
			
			break;
		}
		case PQOPDefine.TK_TYPE_SUM:
		{
			strResult = new String(dSumContent);
			break;
		}
		case PQOPDefine.TK_TYPE_MAXIMUM:
		{
			if (PQOPDefine.CONTENT_MIN < dMaxContent)
			{
				strResult = new String(dMaxContent);
			}
			
			break;
		}
		case PQOPDefine.TK_TYPE_MINIMUM:
		{
			if (PQOPDefine.CONTENT_MAX > dMinContent)
			{
				strResult = new String(dMinContent);
			}
			
			break;
		}
		default:
			break;
	}

	return strResult;
}

PQOPCalculate.prototype._funcToRound = function (strValue, strPeriod, strMethod)
{
	var nWidth = (0 < strPeriod.length) ? parseInt(strPeriod) : 0;

	if (0 == strValue.length || 0 == nWidth)
	{
		return strValue;
	}

	var fValue = parseFloat(strValue);
	var dResult = 0.0;
	
	// 소숫점 위에서..
	if (0 < nWidth)
	{
		// 실제 자릿수 보다 더 크면 0을 리턴한다.
		if (nWidth > strValue.length)
		{
			return "0.0";
		}
		
		var nLeftValue = 1.0;
		
		// 자리수를 설정한다.
		for (var i = 0; i < nWidth ; i=i+1)
		{
			nLeftValue *= 10.0;
		}
		
		var strCh = strMethod.charAt(0);
		switch (strCh)
		{
			case "R": //반올림
			case "r":
				dResult = parseFloat(Math.round(fValue / nLeftValue) * nLeftValue);
				break;
			case "D": //버림
			case "d":
				dResult = parseFloat(Math.floor(fValue / nLeftValue) * nLeftValue);
				break;
			case "U": //올림
			case "u":
				dResult = parseFloat(Math.ceil(fValue / nLeftValue) * nLeftValue);
				break;
		}
	}
	// 소숫점 아래에서
	else
	{
		var nPos = strValue.indexOf(".");
		var dRightValue = 1.0;
		
		// 소숫점이 없는 경우..
		if (-1 == nPos)
		{
			return strValue;
		}

		nWidth = ((-1) * nWidth) - 1;
		var strRight = strValue.substring(nPos + 1);
		if (nWidth >= strRight.length)
		{
			return strValue;
		}
		
		for (var i = 0; i < nWidth ; i=i+1)
		{
			dRightValue *= 10.0;
		}

		var strCh = strMethod.charAt(0);
		switch (strCh)
		{
			case "R": //반올림
			case "r":
				dResult = parseFloat(Math.round(fValue * dRightValue) / dRightValue);
				break;
			case "D": //버림
			case "d":
				dResult = parseFloat(Math.floor(fValue * dRightValue) / dRightValue);
				break;
			case "U": //올림
			case "u":
				dResult = parseFloat(Math.ceil(fValue * dRightValue) / dRightValue);
				break;
		}
		
		//dResult = dResult.toFixed(nWidth);
	}

	return new String(dResult);
}

PQOPCalculate.prototype._getDate = function(strValue)
{
	var nYear = parseInt(("" != strValue.substr(0,4)) ? strValue.substr(0,4):"0");
	var nMonth = parseInt(("" != strValue.substr(4,2)) ? strValue.substr(4,2):"0");
	var nDate = parseInt(("" != strValue.substr(6,2)) ? strValue.substr(6,2):"0");
	
	//Date 객체의 Month는 실제보다 1이 작음
	return new Date(nYear, nMonth-1, nDate);
}

/**
* 날짜(strValue)에 strAmount만큼 더하거나 뺀다.
* @param strValue 연산식에 설정된 날짜입력란의 값
* @param strAmount 
* @return
*/
PQOPCalculate.prototype._funcToDateAmount = function (strValue, strAmount)
{
	//날짜입력란 연산식 함수는 Builder에서 년월일 형식일때만 정상동작하고 있습니다.
	if (null == strValue || 0 == strValue.length || 8 != strValue.length)
	{
		return strValue;
	}
	// 연산식에 설정된 날짜입력란의 값
	//사용자가 어떤형식을 사용할지 모르므로 strValue에 따라서 값을 설정한다.
	var date = this._getDate(strValue);
	var nDate = parseInt("" != strAmount ? strAmount : 0);
	date.setDate(nDate + date.getDate());
	
	var nMonth = parseInt(date.getMonth() + 1);
	var nDate = date.getDate();
	
	 return date.getFullYear().toString() +
		 (10 > parseInt(nMonth)? "0" + nMonth : nMonth).toString() +
		 (10 > parseInt(nDate) ? "0" + nDate : nDate).toString();
}

PQOPCalculate.prototype._funcToDateTerm = function (strDate1, strDate2)
{
	if (null == strDate1 || 0 == strDate1.length || null == strDate2 || 0 == strDate2.length)
	{
		return "";
	}
	
	//날짜입력란 연산식 함수는 Builder에서 년월일 형식일때만 정상동작하고 있습니다.
	if (8 != strDate1.length || 8 != strDate2.length)
	{
		return "";
	}
	var date1 = this._getDate(strDate1);
	var date2 = this._getDate(strDate2);

    var time = 86400000; /* = 1000 * 3600 * 24, 24시간*/
    
    var nTerm = Math.abs(parseInt((date1 - date2)/time));
    return nTerm.toString();
}

PQOPCalculate.prototype._plus = function (strOperand1, strOperand2)
{
	if (!isNaN(strOperand1) && !isNaN(strOperand2))
	{
		strOperand1 = this._changeZero(strOperand1);
		strOperand2 = this._changeZero(strOperand2);
		
		return new String(parseFloat(strOperand1) + parseFloat(strOperand2));
	}
	
	return strOperand1 + strOperand2;
}

PQOPCalculate.prototype._minus = function (strOperand2, strOperand1)
{
	var dOp1 = (0 < strOperand1.length) ? parseFloat(strOperand1) : 0.0;
	var dOp2 = (0 < strOperand2.length) ? parseFloat(strOperand2) : 0.0;
	
	var dResult = dOp2 - dOp1;

	var strResult = new String(dResult);
	
	return strResult;
}

PQOPCalculate.prototype._uminus = function (strOperand1)
{
	var dOp1 = (0 < strOperand1.length) ? parseFloat(strOperand1) : 0.0;

	var dResult = (-1.0) * dOp1;

	return new String(dResult);
}

PQOPCalculate.prototype._multiply = function (strOperand2, strOperand1)
{
	var dOp1 = (0 < strOperand1.length) ? parseFloat(strOperand1) : 0.0;
	var dOp2 = (0 < strOperand2.length) ? parseFloat(strOperand2) : 0.0;
	
	var dResult = dOp2 * dOp1;
	
	return new String(dResult);;
}

PQOPCalculate.prototype._divide = function (strOperand2, strOperand1)
{
	if (0 == strOperand1.length || "0" == strOperand1)
	{
		return "0";
	}

	if (0 == strOperand2.length || "0" == strOperand2)
	{
		return "0";
	}

	var dOp1 = (0 < strOperand1.length) ? parseFloat(strOperand1) : 0.0;
	var dOp2 = (0 < strOperand2.length) ? parseFloat(strOperand2) : 0.0;

	var dResult = dOp2 / dOp1;

	return new String(dResult);;
}

PQOPCalculate.prototype._assign = function (strOperand2, strOperand1)
{
	strOperand1 = strOperand2;

	return strOperand1;
}

PQOPCalculate.prototype._concatenate = function (strOperand2, strOperand1)
{
	return strOperand2 + strOperand1;
}

/**
 * 널이거나 빈문자열이면 "0"을 리턴합니다.
 */
PQOPCalculate.prototype._changeZero = function (strValue)
{
	if (null == strValue || 0 == strValue.length)
	{
		return "0";
	}
	
	return strValue;
}
////////////////////////////////////////
// common method

PQOPCalculate.prototype._isContainColon = function (strTokenString)
{
	if (-1 < strTokenString.indexOf(PQOPDefine.TK_STR_COLON))
	{
		return true;
	}
	
	return false;
}

PQOPCalculate.prototype._getSubVarName = function (strTokenString)
{
	var strTemp = strTokenString;
	
	var nPosition = strTemp.indexOf(".");

	if (PQOPDefine.NOTFOUND != nPosition && nPosition + 1 < strTemp.length)
	{
		strTemp = strTemp.substring(nPosition + 1);
	}
	
	return strTemp;
}

PQOPCalculate.prototype._getGridVarName = function (strTokenString)
{
	var strTemp = strTokenString;

	var nPosition = strTemp.indexOf(".");
	
	if (-1 < nPosition)
	{
		strTemp = strTemp.substring(0, nPosition);
	}
	
	return strTemp;
}

PQOPCalculate.prototype._getScrollName = function (strTokenString)
{
	var strTemp = strTokenString;
	var objPQOPVar = this._getOperationVar(strTokenString);
	var strScrollName = "";
	
	if (objPQOPVar.isBindedAtom())
	{
		strScrollName = objPQOPVar.getScrollName();	
	}
	return strScrollName;
		
}

PQOPCalculate.prototype._getOperationVar = function (strVarName)
{
	var nSharpIndex = strVarName.indexOf("#");
	if(-1 < nSharpIndex)
	{
		strVarName = strVarName.substring(nSharpIndex + 1);
	}
	
	return this.m_objPQOPInfo.getOperationVar(strVarName);
}

PQOPCalculate.prototype._getScrollRowCount = function (strScrollName)
{
	var objScrollAtom = null;
	if (ContainsScrollAtom())
	{
		objScrollAtom = ScrollAtom.getAtom(strScrollName);
	}
	if (null == objScrollAtom)
	{
		return 0;
	}
	
	var nRowCount = objScrollAtom.getLastValidRowIndex();
	
	return nRowCount + 1;
}

PQOPCalculate.prototype._getScrollBindedAtomData = function (nScrollRowIndex, strVarName, strScrollName)
{
	var objScrollAtom = null;
	if (ContainsScrollAtom())
	{
		objScrollAtom = ScrollAtom.getAtom(strScrollName);
	}
	if (null == objScrollAtom)
	{
		return "";
	}
	
	var objBindedAtom = objScrollAtom.getBindedAtom(strVarName, nScrollRowIndex);
	if (null == objBindedAtom)
	{
		return "";
	}
	
	return this._getAtomData(objBindedAtom);
}

PQOPCalculate.prototype._getNormalAtomData = function (strKey)
{
	var objAtom = null;
	if (ContainsInputDataAtom())
	{
		objAtom = InputDataAtom.getAtom(strKey);
	}
	if (null == objAtom && ContainsInputTimeAtom())
	{
		objAtom = InputTimeAtom.getAtom(strKey);
	}
	if (null == objAtom && ContainsComboAtom())
	{
		objAtom = ComboAtom.getAtom(strKey);
	}
	
	return this._getAtomData(objAtom);
}

PQOPCalculate.prototype._getAtomData = function (objAtom)
{
	var strValue = "";
	if (null != objAtom)
	{
		strValue = objAtom.getValue();
	}
	
	return strValue;
}


//----------------------------------------------------------------------------
//----------------------------------------------------------------------------


function PQOPInfo ()
{
	this.m_htOpVarList = new Hashtable();
	this.m_htOpAtomList = new Hashtable();
	
	this.m_arOpVarList = new Array();
	this.m_arOpAtomList = new Array();
}	
	
PQOPInfo.prototype.addVarList = function (strVarName, objPQOPAtom)
{
	this.m_htOpVarList[strVarName] = objPQOPAtom;
	this.m_arOpVarList.push(objPQOPAtom);
}

PQOPInfo.prototype.addAtomList = function (strVarName, objPQOPAtom)
{
	this.m_htOpAtomList[strVarName] = objPQOPAtom;
	this.m_arOpAtomList.push(objPQOPAtom);
}

PQOPInfo.prototype.getOperationVarList = function ()
{
	return this.m_arOpVarList;
}

PQOPInfo.prototype.getOperationAtomList = function ()
{
	return this.m_arOpAtomList;
}

PQOPInfo.prototype.getOperationVar = function (strVarName)
{
	return this.m_htOpVarList[strVarName];
}

PQOPInfo.prototype.getOperationAtom = function (strVarName)
{
	return this.m_htOpAtomList[strVarName];
}

PQOPInfo.prototype.makeVarListToAtom = function (arOperations, strTargetVar)
{
	for (var i = 0; i < arOperations.length; i++)
	{
		var objOperationToken = arOperations[i];
		var nKind = objOperationToken.getTokenType();
		var strOperationUnit = objOperationToken.getTokenString();

		// 변수명 토큰만 유효하다. 
		if (50 == nKind)
		{
			var strVarCode = "";
			var objAtom = null;

			// 그리드변수명.항목명 일 경우
			if (0 <= strOperationUnit.indexOf(".")) 
			{
			}
			else
			{
				// #이 안붙어서 변수명이 저장되도록 변경되었음.
				strVarCode = strOperationUnit;

				if(null != strVarCode && 0 != strVarCode.length)
				{
					if(Utils.isNumber(strVarCode))
					{
						//objAtom = getAtomByCode(strVarCode);
					}
					else
					{
						objAtom = Model.getAtom(strVarCode);
					}
				}
			}

			// 연산식으로 자기자신을 참조하고 있는 경우에는 리스트에서 제외함
			// 빌더 IsAtomInMyOperator() 함수 참조
			if (null != objAtom && (objAtom.getVarName() != strTargetVar))
			{
				var strFromVar = objAtom.getVarName();
				objAtom.setIsOperation(true);
				
				var objOPAtom = this.m_htOpVarList[strFromVar];
				if (null == objOPAtom)
				{
					var objOPAtom = new PQOPAtom();
					objOPAtom.makeVarListInfoReal (objAtom, strTargetVar)
				
					this.addVarList(strFromVar, objOPAtom)
				}
				else
				{
					objOPAtom.addVarListInfo(strTargetVar);
				}
			}
		}
	}
}

/**
 * 연산식 변수아톰에 연계된 아톰리스트에 Atom을 추가하면서 Map을 완성해 나간다.
 */
PQOPInfo.prototype.makeAtomListToVar = function (alOpToken, strTargetVar)
{
	var objOPAtom = this.m_htOpAtomList[strTargetVar];
	
	if (null == objOPAtom)
	{
		var objAtom = Model.getAtom(strTargetVar);
		
		if (null != objAtom)
		{
			objOPAtom = new PQOPAtom();
			objOPAtom.makeAtomListInfoReal (objAtom, alOpToken)
		
			this.addAtomList(strTargetVar, objOPAtom);
		}
	}
	else
	{
		objOPAtom.setTokenList(alOPToken);
	}
}


function PQOPAtom ()
{
	this.m_strVarName = "";
	this.m_strAtomType = "";
	this.m_nScrollType = 0;
	this.m_strScrollName = "";
	
	this.m_arRefOpAtomList = null;
	
	this.m_alTokenList = null;
}

PQOPAtom.TYPE_NORMAL = 0;
PQOPAtom.TYPE_BINDED = 1;
PQOPAtom.TYPE_SCROLL = 2;
PQOPAtom.TYPE_GRIDBIND = 3;
PQOPAtom.TYPE_GRIDEX = 4;
	
PQOPAtom.prototype.setVarName = function (strVarName)
{
	this.m_strVarName = strVarName;
}

PQOPAtom.prototype.setAtomType = function (strAtomType)
{
	this.m_strAtomType = strAtomType;
}

PQOPAtom.prototype.setScrollType  = function (nScrollType)
{
	this.m_nScrollType = nScrollType;
}

PQOPAtom.prototype.setScrollName = function (strScrollName)
{
	this.m_strScrollName = strScrollName;
}

PQOPAtom.prototype.setRefOpAtomList = function (arRefOpAtomList)
{
	this.m_arRefOpAtomList = arRefOpAtomList;
}

PQOPAtom.prototype.setTokenList = function (alTokenList)
{
	this.m_alTokenList = alTokenList;
}


PQOPAtom.prototype.getVarName = function ()
{
	return this.m_strVarName;
}

PQOPAtom.prototype.getAtomType = function ()
{
	return this.m_strAtomType;
}

PQOPAtom.prototype.getScrollType = function ()
{
	return this.m_nScrollType;
}

PQOPAtom.prototype.getScrollName = function ()
{
	return this.m_strScrollName;
}

PQOPAtom.prototype.getRefOpAtomList = function ()
{
	return this.m_arRefOpAtomList;
}

PQOPAtom.prototype.getTokenList = function ()
{
	return this.m_alTokenList;
}


PQOPAtom.prototype.isNormalAtom = function ()
{
	return (PQOPAtom.TYPE_NORMAL == this.m_nScrollType) ? true : false;
}

PQOPAtom.prototype.isBindedAtom = function ()
{
	return (PQOPAtom.TYPE_BINDED == this.m_nScrollType) ? true : false;
}

PQOPAtom.prototype.isScrollAtom = function ()
{
	return (PQOPAtom.TYPE_SCROLL == this.m_nScrollType) ? true : false;
}

PQOPAtom.prototype.makeVarListInfoReal = function (objAtom, strTargetVar)
{
	this.m_strVarName = objAtom.getVarName();
	this.m_strAtomType = objAtom.getAtomType();
	this.m_strScrollName = objAtom.getScrollName();
	
	var nScrollType = objAtom.isScroll() ? 1 : 0;
	this.m_nScrollType = nScrollType;
	
	this.m_arRefOpAtomList = new Array();
	this.m_arRefOpAtomList.push(strTargetVar);
}

PQOPAtom.prototype.addVarListInfo = function (strTargetVar)
{
	var bFound = false;
	
	for (var i = 0; i < this.m_arRefOpAtomList.length; i++)
	{
		if (strTargetVar == this.m_arRefOpAtomList[i])
		{
			bFound = true;
			break
		}
	}
	
	if (false == bFound)
	{
		this.m_arRefOpAtomList.push(strTargetVar);
	}
}

PQOPAtom.prototype.makeAtomListInfoReal = function (objAtom, alOpToken)
{
	this.m_strVarName = objAtom.getVarName();
	this.m_strAtomType = objAtom.getAtomType();
	this.m_strScrollName = objAtom.getScrollName();
	
	var nScrollType = objAtom.isScroll() ? 1 : 0;
	this.m_nScrollType = nScrollType;
	
	this.m_alTokenList = alOpToken;
}


function PQOPInfoManager ()
{
	this.m_objPQOPCompiler = new PQOPCompiler();
}	

PQOPInfoManager.prototype.createPQOPInfo = function (xnOperation)
{
	var objPQOPInfo = new PQOPInfo();
	
	this._setOperationInfo(objPQOPInfo, xnOperation);
	
	return objPQOPInfo;
}


PQOPInfoManager.prototype._setOperationInfo = function (objPQOPInfo, xnOperation)
{
	this._setOpVarList(objPQOPInfo, xnOperation);
	this._setOpAtomList(objPQOPInfo, xnOperation);
}

PQOPInfoManager.prototype._setOpVarList = function (objPQOPInfo, xnOperation)
{
	var strPath = "//OperationVarList/*";
	var xnOpVarList = this._getOperationNodeList(xnOperation, strPath);
	
	var nLen = xnOpVarList.length;
	for (var i = 0; i < nLen; i=i+1)
	{
		var xnOpVar = xnOpVarList[i];
		
		var strVarName = xnOpVar.getAttribute("VarName");
		
		var objAtom = new PQOPAtom();
		
		this._setCommonInfo(objAtom, xnOpVar);
		this._setOpVarInfo(objAtom, xnOpVar);
		
		objPQOPInfo.addVarList(strVarName, objAtom);
	}
}

PQOPInfoManager.prototype._setOpAtomList = function (objPQOPInfo, xnOperation)
{
	var strPath = "//OperationAtomList/*";
	var xnOpAtomList = this._getOperationNodeList(xnOperation, strPath);
	
	var nLen = xnOpAtomList.length;
	for (var i = 0; i < nLen; i=i+1)
	{
		var xnOpAtom = xnOpAtomList[i];
		
		var strVarName = xnOpAtom.getAttribute("VarName");
		
		var objAtom = new PQOPAtom();
		
		this._setCommonInfo(objAtom, xnOpAtom);
		this._setOpAtomInfo(objAtom, xnOpAtom);
		
		objPQOPInfo.addAtomList(strVarName, objAtom);
	}
}

PQOPInfoManager.prototype._setCommonInfo = function (objAtom, xnOpAtom)
{
	var strVarName = xnOpAtom.getAttribute("VarName");
	var strAtomType = xnOpAtom.nodeName;
	var strScrollName = xnOpAtom.getAttribute("ScrollName");
	var nScrollType = this._getScrollType(xnOpAtom);
	
	objAtom.setVarName(strVarName);
	objAtom.setAtomType(strAtomType);
	objAtom.setScrollName(strScrollName);
	objAtom.setScrollType(nScrollType);
}

PQOPInfoManager.prototype._setOpAtomInfo = function (objAtom, xnOpAtom)
{
	var arTokenList = this._getTokenList(xnOpAtom);
	
	objAtom.setTokenList(arTokenList);
}

PQOPInfoManager.prototype._setOpVarInfo = function (objAtom, xnOpVar)
{
	var arRefOpAtomList = this._getRefOpAtomList(xnOpVar);
	
	objAtom.setRefOpAtomList(arRefOpAtomList);
}

PQOPInfoManager.prototype._getScrollType = function (xnOpAtom)
{
	var strScrollType = xnOpAtom.getAttribute("ScrollType");
	
	if (null == strScrollType)
	{
		strScrollType = "0";
	}
	
	var nScrollType = 0;
	try
	{
		nScrollType = parseInt(strScrollType);
	}
	catch (e)
	{
	}
	
	return nScrollType;
}

PQOPInfoManager.prototype._getTokenList = function (xnOpAtom)
{
	var strOperation = xnOpAtom.getAttribute("Operation");
	
	return this.m_objPQOPCompiler.doCompile(strOperation);
}

PQOPInfoManager.prototype._getRefOpAtomList = function (xnOpVar)
{
	var xnRefAtomList = xnOpVar.childNodes;
	var nLen = xnRefAtomList.length;
	
	var arRefAtomList = new Array();
	
	for (var i = 0; i < nLen; i=i+1)
	{
		var xnRefOpAtom = xnRefAtomList[i];
		
		arRefAtomList.push(xnRefOpAtom.getAttribute("VarName"));
	}
	
	return arRefAtomList;
}

PQOPInfoManager.prototype._getOperationNodeList = function (xnOperation, strPath)
{
	if (null == xnOperation)
	{
		return null;
	}
	
	var xnOpList = XmlLib.selectNodeList(xnOperation, strPath);
	
	return xnOpList;
}



//----------------------------------------------------------------------------
//----------------------------------------------------------------------------



/**
 * PQ Operation Token Class
 */
function PQOPToken (strTokenString, nTokenType)
{
	this.m_strTokenString = (null == strTokenString) ? "" : strTokenString;
	this.m_nTokenType = (null == nTokenType) ? -1 : nTokenType;
}	

PQOPToken.prototype.getTokenString = function ()
{
	return Utils.trim(this.m_strTokenString);
}

PQOPToken.prototype.setTokenString = function (strTokenString)
{
	this.m_strTokenString = strTokenString;
}

PQOPToken.prototype.getTokenType = function ()
{
	return this.m_nTokenType;
}

PQOPToken.prototype.setTokenType = function (nTokenType)
{
	this.m_nTokenType = nTokenType;
}

PQOPToken.getTokenType = function (strToken)
{
	if (PQOPDefine.equals(PQOPDefine.TK_STR_ASSIGN, strToken))
		return PQOPDefine.TK_TYPE_ASSIGN;
	else if (PQOPDefine.equals(PQOPDefine.TK_STR_AVERAGE, strToken))
		return PQOPDefine.TK_TYPE_AVERAGE;
	else if (PQOPDefine.equals(PQOPDefine.TK_STR_ADDMARK, strToken))
		return PQOPDefine.TK_TYPE_ADDMARK;
	else if (PQOPDefine.equals(PQOPDefine.TK_STR_COLON, strToken))
		return PQOPDefine.TK_TYPE_COLON;
	else if (PQOPDefine.equals(PQOPDefine.TK_STR_COMMA, strToken))
		return PQOPDefine.TK_TYPE_COMMA;
	else if (PQOPDefine.equals(PQOPDefine.TK_STR_DATEAMOUNT, strToken))
		return PQOPDefine.TK_TYPE_DATEAMOUNT;
	else if (PQOPDefine.equals(PQOPDefine.TK_STR_DATETERM, strToken))
		return PQOPDefine.TK_TYPE_DATETERM;
	else if (PQOPDefine.equals(PQOPDefine.TK_STR_DIVIDE, strToken))
		return PQOPDefine.TK_TYPE_DIVIDE;
	else if (PQOPDefine.equals(PQOPDefine.TK_STR_EQUAL, strToken))
		return PQOPDefine.TK_TYPE_EQUAL;
	else if (PQOPDefine.equals(PQOPDefine.TK_STR_GREATEREQUAL1, strToken))
		return PQOPDefine. TK_TYPE_GREATEREQUAL;
	else if (PQOPDefine.equals(PQOPDefine.TK_STR_GREATEREQUAL1, strToken))
		return PQOPDefine.TK_TYPE_GREATEREQUAL;
	else if (PQOPDefine.equals(PQOPDefine.TK_STR_GREATERTHAN, strToken))
		return PQOPDefine.TK_TYPE_GREATERTHAN;
	else if (PQOPDefine.equals(PQOPDefine.TK_STR_IF, strToken))
		return PQOPDefine.TK_TYPE_IF;
	else if (PQOPDefine.equals(PQOPDefine.TK_STR_L_PAREN, strToken))
		return PQOPDefine.TK_TYPE_L_PAREN;
	else if (PQOPDefine.equals(PQOPDefine.TK_STR_R_PAREN, strToken))
		return PQOPDefine.TK_TYPE_R_PAREN;
	else if (PQOPDefine.equals(PQOPDefine.TK_STR_L_MIDPAREN, strToken))
		return PQOPDefine.TK_TYPE_L_MIDPAREN;
	else if (PQOPDefine.equals(PQOPDefine.TK_STR_R_MIDPAREN, strToken))
		return PQOPDefine.TK_TYPE_R_MIDPAREN;
	else if (PQOPDefine.equals(PQOPDefine.TK_STR_LESSEQUAL1, strToken))
		return PQOPDefine.TK_TYPE_LESSEQUAL;
	else if (PQOPDefine.equals(PQOPDefine.TK_STR_LESSTHAN, strToken))
		return PQOPDefine.TK_TYPE_LESSTHAN;
	else if (PQOPDefine.equals(PQOPDefine.TK_STR_TRUE, strToken))
		return PQOPDefine.TK_TYPE_TRUE;
	else if (PQOPDefine.equals(PQOPDefine.TK_STR_FALSE, strToken))
		return PQOPDefine.TK_TYPE_FALSE;
	else if (PQOPDefine.equals(PQOPDefine.TK_STR_MAXIMUM, strToken))
		return PQOPDefine.TK_TYPE_MAXIMUM;
	else if (PQOPDefine.equals(PQOPDefine.TK_STR_MINIMUM, strToken))
		return PQOPDefine.TK_TYPE_MINIMUM;
	else if (PQOPDefine.equals(PQOPDefine.TK_STR_MINUS, strToken))
		return PQOPDefine.TK_TYPE_MINUS;
	else if (PQOPDefine.equals(PQOPDefine.TK_STR_UMINUS, strToken))
		return PQOPDefine.TK_TYPE_UMINUS;
	else if (PQOPDefine.equals(PQOPDefine.TK_STR_MULTIPLY, strToken))
		return PQOPDefine.TK_TYPE_MULTIPLY;
	else if (PQOPDefine.equals(PQOPDefine.TK_STR_NOTEQUAL1, strToken))
		return PQOPDefine.TK_TYPE_NOTEQUAL;
	else if (PQOPDefine.equals(PQOPDefine.TK_STR_OR, strToken))
		return PQOPDefine.TK_TYPE_OR;
	else if (PQOPDefine.equals(PQOPDefine.TK_STR_AND, strToken))
		return PQOPDefine.TK_TYPE_AND;
	else if (PQOPDefine.equals(PQOPDefine.TK_STR_PLUS, strToken))
		return PQOPDefine.TK_TYPE_PLUS;
	else if (PQOPDefine.equals(PQOPDefine.TK_STR_ROUND, strToken))
		return PQOPDefine.TK_TYPE_ROUND;
	else if (PQOPDefine.equals(PQOPDefine.TK_STR_DATESYSTEM, strToken))
		return PQOPDefine.TK_TYPE_DATESYSTEM;
	else if (PQOPDefine.equals(PQOPDefine.TK_STR_PERCENT, strToken))
		return PQOPDefine.TK_TYPE_PERCENT;
	else if (PQOPDefine.equals(PQOPDefine.TK_STR_SHARP, strToken))
		return PQOPDefine.TK_TYPE_SHARP;
	else if (PQOPDefine.equals(PQOPDefine.TK_STR_SUM, strToken))
		return PQOPDefine.TK_TYPE_SUM;
	else if (PQOPDefine.equals(PQOPDefine.TK_STR_CONCATENATE, strToken))
		return PQOPDefine.TK_TYPE_CONCATENATE;
	else if (-1 < strToken.search(/\d/)) // 정규표현식을 사용하여 문자열에서 숫자가 있는지 확인
		return PQOPDefine.TK_TYPE_DIGIT;
	else
		return PQOPDefine.TK_TYPE_WORD;
}



function PQOPXml (xnOperation)
{
	this.m_xnOperation = xnOperation;
}


PQOPXml.prototype.getOperationVarList = function ()
{
	var strPath = "//OperationVarList/*";
	
	return this._getOperationNodeList(this.m_xnOperation, strPath);
}

PQOPXml.prototype.getOperationAtomList = function ()
{
	var strPath = "//OperationAtomList/*";
	
	return this._getOperationNodeList(this.m_xnOperation, strPath);
}

PQOPXml.prototype.getRefOperationAtomList = function (xnOpVar)
{
	var strPath = "./OperationAtom";
	
	return this._getOperationNodeList(xnOpVar, strPath);
}

PQOPXml.prototype.getOperationVar = function (strVarName)
{
	var strPath = "//OperationVarList/*[@VarName=\"" + strVarName + "\"]";
	
	return this._getOperationNode(this.m_xnOperation, strPath);
}

PQOPXml.prototype.getOperationAtom = function (strVarName)
{
	var strPath = "//OperationAtomList/*[@VarName=\"" + strVarName + "\"]";
	
	return this._getOperationNode(this.m_xnOperation, strPath);
}

PQOPXml.prototype.isScrollAtom = function (xnOp)
{
	var nType = this._getOperationScrollType(xnOp);
	
	if (PQOPXml.TYPE_SCROLL == nType)
		return true;
	
	return false;
}

PQOPXml.prototype.isBindedAtom = function (xnOp)
{
	var nType = this._getOperationScrollType(xnOp);
	
	if (PQOPXml.TYPE_BINDED == nType)
		return true;
	
	return false;
}

PQOPXml.prototype.isNormalAtom = function (xnOp)
{
	var nType = this._getOperationScrollType(xnOp);
	
	if (PQOPXml.TYPE_NORMAL == nType)
		return true;
	
	return false;
}


PQOPXml.prototype._getOperationNodeList = function (xnOperation, strPath)
{
	if (null == xnOperation)
		return null;
	
	return xnOperation.selectNodes(strPath);
}

PQOPXml.prototype._getOperationNode = function (xnOperation, strPath)
{
	if (null == xnOperation)
		return null;
		
	return xnOperation.selectSingleNode(strPath);
}

PQOPXml.prototype._getOperationScrollType = function (xnOp)
{
	var strType = xnOp.getAttribute("ScrollType");

	return parseInt(strType);
}


PQOPXml.TYPE_NORMAL = 0;
PQOPXml.TYPE_BINDED = 1;
PQOPXml.TYPE_SCROLL = 2;






/**
 * PQ Operation Define Class
 */
 
function PQOPDefine ()
{
}

PQOPDefine.equals = function (strDefine, strTokenString)
{
	if (strDefine == strTokenString.toUpperCase())
		return true;
	
	return false;
}

PQOPDefine.EOF 						= -1;	// end of file
PQOPDefine.EOS						= "\0";	// end of string

PQOPDefine.DESCEND					= 1;
PQOPDefine.ASCEND					= 2;
PQOPDefine.NOTFOUND					= -1;
PQOPDefine.SAME						= 0;
PQOPDefine.MINFUNCSIZE				= 4;
	
PQOPDefine.CONTENT_MAX				= 999999999999999;
PQOPDefine.CONTENT_MIN				= -999999999999999;
	
PQOPDefine.EMPTY					= null;
PQOPDefine.SEPARATOR				= ",/+-*!=#<>:(){}|";
PQOPDefine.SEPARATOR1				= ",+-*!%=#<>:()|/{}";

PQOPDefine.TK_CHAR_POINT			= ".";
	
PQOPDefine.TK_TYPE_ERROR			= -1;
	
PQOPDefine.TK_TYPE_ADDMARK			= 0;
PQOPDefine.TK_TYPE_AND				= 1;
PQOPDefine.TK_TYPE_AVERAGE			= 2;
PQOPDefine.TK_TYPE_COMMA			= 3;
PQOPDefine.TK_TYPE_DATEAMOUNT		= 4;
PQOPDefine.TK_TYPE_DATETERM			= 5;
PQOPDefine.TK_TYPE_DIVIDE			= 6;
PQOPDefine.TK_TYPE_EQUAL			= 7;
PQOPDefine.TK_TYPE_GREATEREQUAL		= 8;
PQOPDefine.TK_TYPE_GREATERTHAN		= 9;
PQOPDefine.TK_TYPE_IF				= 10;
PQOPDefine.TK_TYPE_L_PAREN			= 11;
PQOPDefine.TK_TYPE_LESSEQUAL		= 12;
PQOPDefine.TK_TYPE_LESSTHAN			= 13;	
PQOPDefine.TK_TYPE_MAXIMUM			= 14;
PQOPDefine.TK_TYPE_MINIMUM			= 15;
PQOPDefine.TK_TYPE_MINUS			= 16;
PQOPDefine.TK_TYPE_MULTIPLY			= 17;
PQOPDefine.TK_TYPE_NOTEQUAL			= 18;
PQOPDefine.TK_TYPE_OR				= 19;
PQOPDefine.TK_TYPE_PLUS				= 20;
PQOPDefine.TK_TYPE_QUESTION			= 21;
PQOPDefine.TK_TYPE_R_PAREN			= 22;
PQOPDefine.TK_TYPE_ROUND			= 23;
PQOPDefine.TK_TYPE_SPACE			= 24;
PQOPDefine.TK_TYPE_SYSTEMDATE		= 25;
	
PQOPDefine.TK_TYPE_SHARP			= 27;
PQOPDefine.TK_TYPE_PERCENT			= 28;
PQOPDefine.TK_TYPE_EXCLAM			= 29;

PQOPDefine.TK_TYPE_SLASH			= 31;
PQOPDefine.TK_TYPE_L_MIDPAREN		= 32;
PQOPDefine.TK_TYPE_R_MIDPAREN		= 33;
PQOPDefine.TK_TYPE_ASSIGN			= 34;	
PQOPDefine.TK_TYPE_TRUE				= 35;
PQOPDefine.TK_TYPE_FALSE			= 36;
PQOPDefine.TK_TYPE_DATESYSTEM		= 40;
PQOPDefine.TK_TYPE_SUM				= 41;
PQOPDefine.TK_TYPE_COLON			= 42;
PQOPDefine.TK_TYPE_NULL				= 43;
PQOPDefine.TK_TYPE_CONCATENATE		= 44;
	
PQOPDefine.TK_TYPE_GREATEREQUAL1	= 46;
PQOPDefine.TK_TYPE_LESSEQUAL1		= 47;
PQOPDefine.TK_TYPE_NOTEQUAL1		= 48;
PQOPDefine.TK_TYPE_UMINUS			= 49;
	
PQOPDefine.TK_TYPE_ID				= 50;
PQOPDefine.TK_TYPE_WORD				= 51;
PQOPDefine.TK_TYPE_DIGIT			= 52;
PQOPDefine.TK_TYPE_KEYWORD			= 53;
PQOPDefine.TK_TYPE_OPERATOR			= 100;
PQOPDefine.TK_TYPE_FUNCTION			= 200;

PQOPDefine.TK_STR_ADDMARK			= "@";
PQOPDefine.TK_STR_SHARP				= "#";
	
PQOPDefine.TK_STR_AND				= "AND";
PQOPDefine.TK_STR_OR				= "OR";
	
PQOPDefine.TK_STR_AVERAGE			= "A";
PQOPDefine.TK_STR_SUM				= "S";
PQOPDefine.TK_STR_MAXIMUM			= "MX";
PQOPDefine.TK_STR_MINIMUM			= "MN";
PQOPDefine.TK_STR_DATEAMOUNT		= "DA" ;
PQOPDefine.TK_STR_DATETERM			= "DT";
PQOPDefine.TK_STR_DATESYSTEM		= "D";
PQOPDefine.TK_STR_ROUND				= "RD";
PQOPDefine.TK_STR_IF				= "I";

PQOPDefine.TK_STR_DOT				= ".";	
PQOPDefine.TK_STR_COLON				= ":";
PQOPDefine.TK_STR_COMMA				= ",";
PQOPDefine.TK_STR_MULTIPLY			= "*";
PQOPDefine.TK_STR_DIVIDE			= "/";
PQOPDefine.TK_STR_MINUS				= "-";
PQOPDefine.TK_STR_UMINUS			= "-_U";
PQOPDefine.TK_STR_PLUS				= "+";
PQOPDefine.TK_STR_PERCENT			= "%";
PQOPDefine.TK_STR_ASSIGN			= "=";
PQOPDefine.TK_STR_CONCATENATE		= "|";
	
PQOPDefine.TK_STR_EQUAL				= "==";	
PQOPDefine.TK_STR_GREATERTHAN		= ">";
PQOPDefine.TK_STR_LESSTHAN			= "<";
PQOPDefine.TK_STR_GREATEREQUAL1		= ">=";
PQOPDefine.TK_STR_LESSEQUAL1		= "<=";
PQOPDefine.TK_STR_NOTEQUAL1			= "<>";
	
PQOPDefine.TK_STR_L_PAREN			= "(";
PQOPDefine.TK_STR_R_PAREN			= ")";
PQOPDefine.TK_STR_R_MIDPAREN		= "}";
PQOPDefine.TK_STR_L_MIDPAREN 		= "{";
	
PQOPDefine.TK_STR_TRUE				= "TRUE";
PQOPDefine.TK_STR_FALSE				= "FALSE";
