#include "stdafx.h"

#include "aplNetStepData.h"


#define MAKE_LAST_LINE_EX(par)


//////////////////////////////////////////////////////////
// Класс расширенных функций работы с запросами

CaplLoadData::CaplLoadData(CaplNetStepData* data):m_iStartQueryIndex(0)
{
	m_data=data;
	m_placement = 0;
	source = _T("");
	ClearQuery();
}

CaplLoadData::CaplLoadData(CaplNetStepData* data, CString _source) : m_iStartQueryIndex(0)
{
	m_data = data;
	m_placement = 0;
	source = _source;
#ifdef _DEBUG
	if(_source == _T(""))
	{
		AfxMessageBox(
			CString(APL_T("Крайне рекомендуем объявлять переменную запроса CaplLoadData  с использованием макроса DEF_SOURCE, например так:\r\n CaplLoadData ld(data,DEF_SOURCE);\r\n"))
			+ APL_T("Это сильно облегчает поиск ошибок при сбойных запросах"));
	}
#endif
	ClearQuery();
}

CaplLoadData::~CaplLoadData()
{
	m_data = NULL;
}

void CaplLoadData::SetStartedQueryIndex(int iStartIndex)
{
	ASSERT(iStartIndex > -1);
	m_iStartQueryIndex = iStartIndex;
}

//	очистка массива запросов
bool CaplLoadData::ClearQuery()
{
	if(m_data == NULL) return false;
	m_queries.Clear();
	m_critical_err = false;
	m_err_string_quer = _T("");
	m_used_id.RemoveAll();
	m_used_id.bAutoSort = true;
	return true;
}//bool CaplLoadData::ClearQuery()


bool CaplLoadData::TestFindId(int &id, TCHAR type, int prev_id)
{
	int i_type = (int)type;
	int i_type_found;
	bool flag_add = true;
	CString str;

	if(id == -1)
	{
		if(m_used_id.Size == 0)
		{
			id = 0;
		}
		else
		{
			//Даровских А.В. Этот код позволяет делать корректное построение массива запросов
			int iMaxId(m_iStartQueryIndex-1), i;
			for(i=0; i <m_used_id.Size; ++i)
			{
				if(iMaxId < m_used_id.Data[i].in)
					iMaxId = m_used_id.Data[i].in;
			}
			id = iMaxId + 1;
		}
	}
	else
	{
		// Проверяем на запрос с тем же номером и другим типом
		i_type_found = m_used_id.QGetByIn(id);
		if(i_type_found != -1)
		{
			//такс, такой номер запроса уже есть. Надо сверять типы
			// запрос с таким id нашли.
			// Если это запрос типа 'a' или 'u' - тогда найденный может быть только такого же типа
			if(type == _T('a') || type== _T('u'))
			{
				if(i_type_found != i_type)
				{
					str.Format( APL_T("\nДобавляется запрос: id: %i  тип: '%c'. Ошибка: в текущем массиве запросов уже есть запрос с таким же номером и с другим типом ('%c')!!!\n"),
						id, i_type, i_type_found); 
					m_err_string_quer += str;
					return false;
				}
				else
				{
					// этот номер запроса уже есть в массиве номеров запросов
					flag_add = false;
				}
			}
			// идентификтаоры запросов типа 'd' и 'r' тоже могут повторяться - но тоже только с таким же типом!
			else if(type== _T('d') || type== _T('r'))
			{
				if(i_type_found != i_type)
				{
					str.Format( APL_T("\nДобавляется запрос: id: %i  тип: '%c'. Ошибка: в текущем массиве запросов уже есть запрос с таким же номером и с другим типом ('%c')!!!\n"),
						id, i_type, i_type_found);
					m_err_string_quer += str;
					return false;
				}
				else if(prev_id == id)
				{
					str.Format( APL_T("\nДобавляется запрос: id: %i  тип: '%c'. Ошибка: номер запроса совпадает с номером предыдущего запроса, по которому он должен выполняться!!!\n"),
						id, i_type);
					m_err_string_quer += str;
					return false;
				}
				else
				{
					// этот номер запроса уже есть в массиве номеров запросов
					flag_add = false;
				}
			}
			// а идентификтаоры запросов остальных типов не могут повторяться
			else
			{
				TCHAR c1 = i_type;
				TCHAR c2 = (TCHAR)i_type_found;
				TCHAR c3 = i_type;
				str.Format( APL_T("\nДобавляется запрос: id: %i  тип: '%c'. Ошибка: в текущем массиве запросов уже есть другой запрос с таким же номером и с типом '%c', а для типа '%c' каждый запрос должен иметь уникальный номер!!!\n"),
					id, c1, c2, c3);
				m_err_string_quer += str;
				return false;
			}
		}

	}
	// если такой id запроса еще не заюзан - добавляем в список
	if(flag_add) m_used_id.Add(id, i_type);

	return true;
}

// запрос типа 'a' с автоматической генерацией номера
//ЯАИ: убрано по причине, описанной в headere
/*int CaplLoadData::AddQuery(CaplInstance *inst, bool load_all_attr)
{
	return AddQuery(-1,inst,load_all_attr);
}*/


// запрос типа 'a' с предопределенным номером
int CaplLoadData::AddQuery(int id, CaplInstance *inst, bool load_all_attr)
{
	if(m_data== 0)
	{
		m_err_string_quer += APL_T("Объект кеша для запроса не инициилизирован!\n");
		return -1;
	}
	if(inst== 0)  
	{
		CString str; str.Format( APL_T("Неверный instance (inst== 0); запрос номер %i\n"), id);
		m_err_string_quer += str; return -1;
	}
	if(inst->GetTemporary()) 
	{
		CString str; str.Format( APL_T("Неверный instance (temporary==true); запрос номер %i\n"), id);
		m_err_string_quer += str; return -1;
	}
	if(inst->GetId() == 0) 
	{
		CString str; str.Format( APL_T("Неверный instance (id== 0); запрос номер %i\n"), id);
		m_err_string_quer += str; return -1;
	}
	if(inst->GetType() == 0) 
	{
		CString str; str.Format( APL_T("Неверный instance (type== 0)(inst_id==%i); запрос номер %i\n"), inst->GetId(), id);
		m_err_string_quer += str; return -1;
	};
	if(!TestFindId(id, _T('a'), 0))
	{
		return -1;
	}

	apl_NET_LoadQuery* query = new apl_NET_LoadQuery(id, 'a', inst->GetId(), inst->GetType()->id, 0, true, load_all_attr);
	if(query == NULL){ return -1;}
	m_queries.Add(query);

	return query->id;
}

// запросы с именами атрибута и энтити
int CaplLoadData::AddQuery(TCHAR _type, long _inst_or_id, LPCTSTR ent_name, LPCTSTR attr_name, bool _in_result, bool _load_all_attrs)
{
	if(m_data == 0) return -1;
	CaplEntity *ent = m_data->GetEntityBN(ent_name);
	if(ent == 0) {m_err_string_quer += APL_T("Отсутсвует entity ") + CString(ent_name) + APL_T(" Обновите словарь БД!\n"); m_critical_err = true; return -1;}

	CaplAttr *attr = m_data->GetAttrDefinition(ent, attr_name);
	if(attr == 0) {m_err_string_quer += APL_T("Отсутствует атрибут ") + CString(attr_name) + APL_T(" у entity ") + ent->name + APL_T(" Обновите словарь БД!\n"); m_critical_err = true; return -1;}
	return AddQuery(_type, _inst_or_id, ent, attr, _in_result, _load_all_attrs);
}

// запросы с атрибутом и энтити в виде цапельных классов
int CaplLoadData::AddQuery(TCHAR _type, long _inst_or_id, CaplEntity *ent, CaplAttr *attr, bool _in_result, bool _load_all_attrs)
{
	if(_inst_or_id == -1){m_err_string_quer += APL_T("Неверный идентификатор предыдущего запроса!\n"); return -1;}
	return AddQuery(-1, _type, _inst_or_id, ent, attr, _in_result, _load_all_attrs);
}

//	добавление запроса в массив запросов 
int CaplLoadData::AddQuery(TCHAR _type, long _inst_or_id, long _ent_id, long _attr_id, bool _in_result, bool _load_all_attrs)
{
	if(m_data == NULL) return -1;
	CaplEntity* ent = 0;
	CaplAttr* attr = 0;
	if(_ent_id != 0)
	{
		ent = m_data->GetEntityById(_ent_id);
	}		
	if(_attr_id != 0)
	{
		attr = m_data->GetAttrDefinitionById(_attr_id);
	}
	return AddQuery(-1, _type, _inst_or_id, ent, attr, _in_result, _load_all_attrs);

}//int CaplLoadData::AddQuery(char _type,long _inst_or_id,long _ent_id,long _attr_id, bool _in_result)


/** добавление запроса типа 'd' или 'r' рекурсивно по структуре attr_path */
int CaplLoadData::AddQuery(int  prev_id, AttrPath attr_path)
{
	if(attr_path.attr==0) return -1;
	if(attr_path.type!=_T('d') && attr_path.type!=_T('r')) return -1;
	int curr_id = AddQuery(attr_path.type, prev_id, attr_path.ent, attr_path.attr, true, attr_path.next_step.size()==0);
	for ( std::list<AttrPath>::iterator it=attr_path.next_step.begin(); it!= attr_path.next_step.end(); ++it )
		AddQuery(curr_id, *it);
		
	return curr_id;
}


// самый многофункцианальный добавлений, который собсвенно и добавляет
// с проверками
int CaplLoadData::AddQuery(int  id, TCHAR _type, long _inst_or_id, CaplEntity *ent, CaplAttr *attr, bool _in_result, bool _load_all_attrs)
{
	CString str;

	if(m_data == NULL) return -1;
	if(_inst_or_id == -1) return -1;
	MAKE_LAST_LINE_EX(m_data);

	// проверяем номер запроса на уникальность, или генерим автоматический номер
	if(!TestFindId(id, _type, _inst_or_id))
	{
		return -1;
	}
	if((ent == 0) && (attr == 0) && (_inst_or_id == 0) && (_type != _T('u'))) 
	{
		str.Format( APL_T("\nОшибка: все параметры запроса с id %i фактически пустые! Если передавались номера или имена атрибутов и типов - они не найдены в текущем словаре, обновите словарь"),id);
		
		m_err_string_quer += str;
	}
#ifdef _DEBUG
	if(m_queries.GetSize() == 0 && (_type == _T('d') || _type == _T('r') || _type == _T('u')))
	{
		AfxMessageBox(APL_T("Первым подзапросом не может быть запрос типа '") + CString(_type) + 
			APL_T("'!\r\nПеред заполнением/выполнением запроса проверяйте, что экстент не пустой!"));
		return -1;
	}
#endif

	//Всякие проверки
	//На тип запроса
	if(_type != _T('a') && _type != _T('e') && _type != _T('i') && _type != _T('b') && _type != _T('d') && _type != _T('r') && _type != _T('u'))
	{
		str.Format(APL_T(" _type '%c' not defined !!!"), _type); 
		str.Format( APL_T("Неверный запрос с id %i - тип запроса '%c' Не определен !!!\n"), id, _type);
		m_err_string_quer += str;
		return -1;
	}
	if((_type == _T('a')) && (_inst_or_id == 0))
	{
		str.Format( APL_T(" (_type== 'a')&&(_inst_or_id== 0) !!!"));
		str.Format( APL_T("Неверный запрос с id %i - при типе запроса 'a' параметр _inst_or_id не может быть пустым !!!\n"), id);
		m_err_string_quer += str;
		return -1;
	}
	if(_type == _T('i'))
	{
		if(_inst_or_id == 0)
		{
			str.Format(_T(" (_type== 'i')&&(_inst_or_id== 0) !!!"));
			str.Format( APL_T("Неверный запрос с id %i - при типе запроса 'i' параметр _inst_or_id не может быть пустым !!!\n"), id);
			m_err_string_quer += str;
			return -1;
		}
		if(attr== 0)
		{
			str.Format(_T(" (_type== 'i')&&(attr== 0) !!!"));
			str.Format( APL_T("Неверный запрос с id %i - при типе запроса 'i' атрибут должен быть задан !!!\n"), id);
			m_err_string_quer += str;
			return -1;
		}
	}
	if(_type == _T('b'))
	{
		if(_inst_or_id == 0)
		{
			str.Format(_T(" (_type== 'b')&&(_inst_or_id== 0) !!!"));
			str.Format( APL_T("Неверный запрос с id %i - при типе запроса 'b' параметр _inst_or_id не может быть пустым !!!\n"), id);
			m_err_string_quer += str;
			return -1;
		}
		if(attr == 0)
		{
			str.Format(_T(" (_type== 'b')&&(attr== 0) !!!"));
			str.Format( APL_T("Неверный запрос с id %i - при типе запроса 'b' атрибут должен быть задан !!!\n") ,id);
			m_err_string_quer += str;
			return -1;
		}
		if(ent == 0)
		{
			str.Format(_T(" (_type== 'b')&&(ent== 0) !!!"));
			str.Format( APL_T("Неверный запрос с id %i - при типе запроса 'b' ентити должена быть задана !!!\n"), id);
			m_err_string_quer += str;
			return -1;
		}
	}
	if(_type == _T('d'))
	{
		if(_inst_or_id < 0)
		{
			str.Format(_T(" (_type== 'd')&&(_inst_or_id<0) !!!"));
			str.Format( APL_T("Неверный запрос с id %i - при типе запроса 'd' должен быть определен параметр _inst_or_id!!!\n"), id);
			m_err_string_quer += str;
			return -1;
		}
		if(attr == 0)
		{
			str.Format(_T(" (_type=='d')&&(attr== 0) !!!"));
			str.Format( APL_T("Неверный запрос с id %i - при типе запроса 'd' атрибут должен быть задан !!!\n"), id);
			m_err_string_quer += str;
			return -1;
		}
	}
	if(_type == _T('r'))
	{
		if(_inst_or_id < 0)
		{
			str.Format(_T(" (_type=='r')&&(_inst_or_id<0) !!!"));
			str.Format( APL_T("Неверный запрос с id %i - при типе запроса 'r' должен быть определен параметр _inst_or_id!!!\n"), id);
			m_err_string_quer += str;
			return -1;
		}
		if(attr == 0)
		{
			str.Format(_T(" (_type=='r')&&(attr== 0) !!!"));
			str.Format( APL_T("Неверный запрос с id %i - при типе запроса 'r' атрибут должен быть задан !!!\n"), id);
			m_err_string_quer += str;
			return -1;
		}
		if(ent == 0)
		{
			str.Format(_T(" (_type=='r')&&(ent== 0) !!!"));
			str.Format( APL_T("Неверный запрос с id %i - при типе запроса 'r' ентити должена быть задана !!!\n"), id);
			m_err_string_quer += str;
			return -1;
		}
	}
	if(_type == _T('u'))
	{
		if(_inst_or_id < 0)
		{
			str.Format(_T(" (_type== 'u')&&(_inst_or_id<0) !!!"));
			str.Format( APL_T("Неверный запрос с id %i - при типе запроса 'u' должен быть определен параметр _inst_or_id!!!\n"), id);
			m_err_string_quer += str;
			return -1;
		}
		if(attr != 0 || ent != 0)
		{
			str.Format(_T(" (_type== 'u')&&(attr != 0 || ent != 0) !!!"));
			str.Format( APL_T("Неверный запрос с id %i - при типе запроса 'u' можно задавать только идентификатор предыдущего запроса. Обновите словарь !!!\n"), id);
			m_err_string_quer += str;
			return -1;
		}
	}

	// переводим атрибут и ентити в их идентификаторы
	long _ent_id = (ent== 0) ? 0 : ent->id;
	long _attr_id = (attr == 0) ? 0 : attr->id;



	MAKE_LAST_LINE_EX(m_data);
	apl_NET_LoadQuery* query = NULL;
#ifdef _UNICODE
	char __type = ' ';
	switch(_type)
	{
	case L'a':
		__type = 'a';
		break;
	case L'e':
		__type = 'e';
		break;
	case L'i':
		__type = 'i';
		break;
	case L'd':
		__type = 'd';
		break;
	case L'b':
		__type = 'b';
		break;
	case L'r':
		__type = 'r';
		break;
	case L'u':
		__type = 'u';
		break;
	default:
		ASSERT(false);
	}
	query = new apl_NET_LoadQuery(id, __type, _inst_or_id, _ent_id, _attr_id, _in_result ,_load_all_attrs);
#else
	query = new apl_NET_LoadQuery(id, _type, _inst_or_id, _ent_id, _attr_id, _in_result ,_load_all_attrs);
#endif
	if(query == NULL){return -1;}
	m_queries.Add(query);

	return id;
}

//	выполнение текущего массива запросов
bool CaplLoadData::LoadEx(bool clear_query, aplExtent * ext_out, LPCTSTR wait_text, bool hide_error)
{
	CaplStackLogger logger(__APL_FUNC__);

	bool ret_code = LoadExNoInstUserDataTimeInfo(clear_query, ext_out, wait_text, hide_error);

	// Если сервер Oracle
	if (m_data->CanLoadInstUserDataTimeInfo() && (m_data->m_TypeSrv[0] == _T('O') || m_data->m_TypeSrv[0] == _T('P')))
	{
		// Загрузить в CAplInstance дополнительные служебные данные: время создания/изменения, пользователь
		int iSize = m_data->m_last_load_result.GetSize();
		if (iSize == 0)
			return ret_code;

		CaplTAggr<SInstInfo*,SInstInfo*,APLAGGR_AUTOKILLREF> data;
		CaplInstance* pInst = NULL;
		SInstInfo* el;

		int i;
		for (i=0; i < iSize; ++i)
		{
			pInst = m_data->m_last_load_result.GetAt(i);
			if (pInst == NULL)
				continue;
			if (pInst->GetId() == 0)
				continue;

			el = new SInstInfo(pInst);
			data.Add(el);
		}

		iSize = data.GetSize();
		if (iSize == 0)
			return ret_code;

		ret_code &= m_data->NET_LoadInstUserDataTimeInfo(data, wait_text);

		SInstInfo* pEl = NULL;
		for (i=0; i < iSize; ++i)
		{
			pEl = data[i];
			pEl->inst->SetCreateDate(pEl->create_date);
			pEl->inst->SetCreateUser(pEl->create_user);
			pEl->inst->SetUpdateDate(pEl->update_date);
			pEl->inst->SetUpdateUser(pEl->update_user);
		}

		data.Clear();
	}

	return ret_code;
}

//	выполнение текущего массива запросов (с загрузкой служебных данных для instance, кроме данных о редактировании: время создания/изменения, пользователь)
bool CaplLoadData::LoadExNoInstUserDataTimeInfo(bool clear_query, aplExtent * ext_out, LPCTSTR wait_text, bool hide_error)
{
	CaplStackLogger logger(__APL_FUNC__);

	if(m_data == NULL) return false;
	if(m_queries.Size == 0)
	{
		//		m_data->SetAPLErrorWithFileInfo(APL_NET_CLI_MISSED_DATA);
		return false;
	}
	MAKE_LAST_LINE_EX(m_data);
	if(m_err_string_quer != _T(""))
	{
		bool old_hide = m_data->m_hide_mode;
		m_data->m_hide_mode = true;
		CString str;
		str= APL_T("При подготовке данных для запроса возникли следующие ошибки:\n")+
			m_err_string_quer + APL_T("\nИсточник запроса: ") + this->source;

		if(this->source == _T(""))
		{
			str += APL_T("<неизвестно>\r\nРекомендуем определять объект загрузки как\r\n CaplLoadData ld(data,DEF_SOURCE);\r\n В таком случае при ошибке в логах будет указано место вызова, что облегчит отладку");
		}
		m_data->m_ErrorAnnotation = str; 
		m_data->SetLastErrorEx(APL_NET_CLI_ERROR_PROCESSED, false, _T(__FILE__), __LINE__);

		logger.SetFunctionInfo(str);

		m_data->m_hide_mode = old_hide;
		if(m_data->m_hide_mode && m_critical_err) return false;
		if(!m_data->m_hide_mode)
		{
			AfxMessageBox(m_data->GetLastErrorDescription()+ APL_T("\n\nОписание ошибки помещено в лог приложения.\n\nЗапрос будет выполнен не полностью."), MB_OK);
		}
	}
	if(m_queries.Size > 10000)
	{
		CString str;
		str.Format(APL_T("Слишком много запросов в одной загрузке (%i).\n Необходимо изменить вызывающую программу\n Запрос не будет выполнен"),m_queries.Size);
		str += APL_T("\nИсточник запроса: ") + this->source;
		if(this->source == _T(""))
		{
			str += APL_T("<неизвестно>\r\nРекомендуем определять объект загрузки как\r\n CaplLoadData ld(data,DEF_SOURCE);\r\n В таком случае при ошибке в логах будет указано место вызова, что облегчит отладку");
		}
		m_data->m_ErrorAnnotation = str; 
		m_data->SetLastErrorEx(APL_NET_CLI_ERROR_PROCESSED, false, _T(__FILE__), __LINE__);

		logger.SetFunctionInfo(str);

		if(clear_query)
		{
			ClearQuery();
		}
		return false;
	}
	int i = 0;
	apl_NET_LoadQuery* arr_query=NULL;
	arr_query = new apl_NET_LoadQuery[m_queries.Size];

	for(i=0; i <m_queries.Size; i++)
	{
		apl_NET_LoadQuery *src = m_queries[i];
		apl_NET_LoadQuery &dst = arr_query[i];
		dst.Set(src);
		/*
		dst.id=src->id;
		dst.type=src->type;
		dst.inst_or_id=src->inst_or_id;
		dst.ent_id=src->ent_id;
		dst.attr_id=src->attr_id;
		dst.in_result=src->in_result;
		*/

	}
	bool last_err_mess_mode = m_data->m_NetErrorMessageMode;
	if(hide_error)
	{
		m_data->SetNetErrorMessageMode(false);
	}
	bool ret_code = m_data->NET_Load(arr_query, m_queries.Size, ext_out ,m_placement, wait_text, source);

	delete []arr_query;
	if(clear_query)
	{
		ClearQuery();
	}
	m_data->SetNetErrorMessageMode(last_err_mess_mode);
	return ret_code;
}//bool CaplLoadData::LoadEx(bool clear_query)



/** 
	Вспомогательный класс, предназначен для входа в AdminMode и гарантированного выхода из него
*/

CaplAdminModeProvider::CaplAdminModeProvider(CaplNetStepData *data, LPCTSTR _username, LPCTSTR _pwd, LPCTSTR _source)
{
	m_my_admin = false;
	m_data = data;
	m_source = _source;

#ifdef _DEBUG
	if(m_source == _T(""))
	{
		AfxMessageBox(CString(APL_T("Крайне рекомендуем объявлять переменную класса CaplAdminModeProvider с использованием макроса DEF_SOURCE, например так:\r\n CaplAdminModeProvider amp(data, .. , .. ,DEF_SOURCE);\r\n"))
			+ APL_T("Это сильно облегчает поиск ошибок при сбойных вызовах"));
	}
#endif

	StartAdminMode(_username, _pwd, _source);
}


CaplAdminModeProvider::CaplAdminModeProvider(CaplNetStepData *data, int code1, int code2, LPCTSTR _source)
{
	m_my_admin = false;
	m_data = data;
	m_source = _source;

#ifdef _DEBUG
	if(m_source == _T(""))
	{
		AfxMessageBox(CString(APL_T("Крайне рекомендуем объявлять переменную запроса  CaplAdminModeProvider с использованием макроса DEF_SOURCE, например так:\r\n CaplAdminModeProvider amp(data, .. , .. ,DEF_SOURCE);\r\n"))
			+ APL_T("Это сильно облегчает поиск ошибок при сбойных вызовах"));
	}
#endif
	StartAdminMode(code1, code2, _source);

}
CaplAdminModeProvider::CaplAdminModeProvider()
{
	m_data = 0; m_my_admin = false;
}; 


CaplAdminModeProvider::~CaplAdminModeProvider()
{
	ExitAdminMode();	
}

bool CaplAdminModeProvider::StartAdminMode(LPCTSTR _username, LPCTSTR _pwd, LPCTSTR _source)
{
	if(m_data == 0) return false;
	if(m_data->GetAdminMode())return false;
	if(m_data->NET_StartAdminMode2(_username, _pwd, _source))
	{
		m_my_admin = true;
		return true;
	}
	return false;
}

bool CaplAdminModeProvider::StartAdminMode(int code1, int code2, LPCTSTR _source)
{
	if(m_data == 0) return false;
	if(m_data->GetAdminMode())return false;
	if(m_data->NET_StartAdminMode2(code1, code2, _source))
	{
		m_my_admin = true;
		return true;
	}
	return false;
}

bool CaplAdminModeProvider::ExitAdminMode()
{
	if(m_data == 0) return false;
	if(m_my_admin && m_data->GetAdminMode())
	{
		if(m_data->NET_ExitAdminMode())
		{
			m_my_admin = false;
			return true;
		}
	}
	return false;
}