// StepData.cpp


#include "StdAfx.h"
#include "StepData.h"
#include <algorithm>
#include <sstream> // для aplBuildDateTime2Odt


#ifdef _MFC_VER
	#include "Resource.h"
	#include "EfLoadDlg.h"
#endif

#define  ALLOW_COPY_ATTR   // Разрешить наличие копий атрибутов в доп. схеме

#define USE_CAPLMAP_MAP_IN_SAVE_TO_DATA_BUF 

#undef APL_THROW_LEVEL
#define APL_THROW_LEVEL SaplErrorDescriptionLevel_CaplData

#define MAX_FILE_LENGTH_32_BIT 4294967296-1

#define cMinId4ExtSchema 40000 // минимальный ID для дополнительных словарей

// стандартный формат c юникодовыми строками
// пишется в буфер в однобайтовом виде!
static char apl_step_format_id_u2[] ="apl_step_data Vu0.2";
// формат c юникодовыми строками и с дописыванием БЛОБ-ов  в конец файла после атрибутов. Разделитель - 0xFFFFFFFF
// пишется в буфер в однобайтовом виде!
static char apl_step_format_id_u3[]="apl_step_data Vu0.3";

// многолетний стандартный формат
static char apl_step_format_id_2[] = "apl_step_data V 0.2";
// формат с дописыванием БЛОБ-ов  в конец файла после атрибутов. Разделитель - 0xFFFFFFFF
static char apl_step_format_id_3[] = "apl_step_data V 0.3";

/*
static char apl_text_format_id[]="apl_text_data V 0.1";
static char apl_text_format_id_u[]="apl_text_data Vu0.1";
*/

static char apl_text_format_mask[]="apl_text_data V";
static char apl_step_format_mask[]="apl_step_data V";

double MaxDoubleVal = 10E307;

// При установке в false - во внутренних циклах и т.п. перестает обрабатывать
// сообщения WM_PAINT. Флаг добавлен для ILS.
bool global_process_wm_paint=true;

#ifdef _MFC_VER

void PumpTimerPaintStepData(DWORD &dwLastPaintTickCount, bool bPaint=true, bool bTimer=true, bool bSoket=false)
{
	if(!global_process_wm_paint) return;

	DWORD tc=GetTickCount();

	if( (tc >= dwLastPaintTickCount) && ((tc-dwLastPaintTickCount)<1000 ) ) return; // Не чаще раза в сек.

	MSG msg; 
	if(bPaint)
	{
		while (::PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT,
			PM_REMOVE|  PM_NOYIELD)) 
			DispatchMessage(&msg);
	}
	if(bTimer)
	{
		while (::PeekMessage(&msg, NULL, WM_TIMER, WM_TIMER,
			PM_REMOVE|  PM_NOYIELD)) 
			DispatchMessage(&msg);
	}
	if(bSoket)
	{
		while (::PeekMessage(&msg, NULL, WM_SOCKET_NOTIFY, WM_SOCKET_NOTIFY,
		PM_REMOVE|  PM_NOYIELD)) 
		DispatchMessage(&msg);
	}
	dwLastPaintTickCount=GetTickCount();
}
#define QT_THREADS_DENY_FROM_READ
#define QT_THREAD_ENTER_CS
#define QT_THREAD_LEAVE_CS

#else

#define PumpTimerPaintStepData(par1, ...) {;}
#define QT_THREADS_DENY_FROM_READ if(m_lock_readonly.Get()>0){ APL_THROW(APLAPIERR_UNCORRECT_STATE);}
#define QT_THREAD_ENTER_CS m_ThreadsProtect.EnterAplCriticalSection();
#define QT_THREAD_LEAVE_CS m_ThreadsProtect.LeaveAplCriticalSection();
#endif

aplValueType String2aplValueType(LPCTSTR type_name)
{
	CString buf=type_name;
	buf.MakeUpper();
	if(buf==_T("INSTANCE")) return aplINSTANCE;
	if(buf==_T("AGGR")) return aplAGGR;
	if(buf==_T("INTEGER")) return aplINTEGER;
	if(buf==_T("REAL")) return aplREAL;
	if(buf==_T("STRING")) return aplSTRING;
	if(buf==_T("BOOLEAN")) return aplBOOL;
	if(buf==_T("LOGICAL")) return aplLOGICAL;
	if(buf==_T("BLOB")) return aplBLOB;
	if(buf==_T("BINARY")) return aplBINARY;
	if(buf==_T("SELECT")) return aplSELECT;
	if(buf==_T("ENUMERATION")) return aplENUMERATION;
	if(buf==_T("INTEGRATEDBIN")) return aplINTEGRATEDBIN;
	//if(buf==_T("")) return ;
	return aplNOTYPE;
}

void aplValue2String(aplValueType type,CString &str)
{
	str=_T("");
	switch(type)
	{
	case aplINSTANCE: str=_T("instance"); break;
	case aplAGGR: str=_T("aggr"); break;
	case aplINTEGER: str=_T("integer"); break;
	case aplREAL: str=_T("real"); break;
	case aplSTRING: str=_T("string"); break;
	case aplBOOL: str=_T("boolean"); break;
	case aplLOGICAL: str=_T("logical"); break;
	case aplBINARY: str=_T("binary"); break;
	case aplBLOB: str=_T("blob"); break;
	case aplINTEGRATEDBIN: str=_T("integratedbin"); break;
	case aplSELECT: str=_T("select"); break;
	case aplENUMERATION: str=_T("enumeration"); break;
	case aplNOTYPE: str=_T("notype"); break;
	}
}

//************************************************************************
bool SaveToTextFileDebug(CaplStepData &data, LPCTSTR file_name)
{
	if(aplIsStringEmpty(file_name)) {/*SetLastErrorWithFileInfo(APLAPIERR_BADFILENAME);*/ return false;}
//	SetLastError(0);
	int i;
//	int j,instmapsize;
	CString buf,buf1;
	CaplFile file;
#ifdef _UNICODE
	if(!file.Open(file_name,CaplFile::modeCreate|CaplFile::typeUnicode|CaplFile::modeWrite))
		{/*SetLastErrorWithFileInfo(APLAPIERR_FILE_IO);*/ return false;}
#else // #ifdef _UNICODE
	if(!file.Open(file_name,CaplFile::modeCreate|CaplFile::modeWrite))
		{/*SetLastErrorWithFileInfo(APLAPIERR_FILE_IO);*/ return false;}
#endif // #ifdef _UNICODE
	
//	pCaplInstance *instmap=0;
//	// Внимание! instances всегда должен быть отсортирован по id!
//	//
//	//CSortClass::SortInstByInst(instances);//qSortExtent(*(aplExtent*)&instances); 
//	instmapsize=instances.Size;
//	instmap=new pCaplInstance[instmapsize];
//
//	for(i=0;i<instmapsize;i++) instmap[i]=instances[i];
//	CSortClass::SortArrayByInst(&instmap,instmapsize);

	file.WriteString(_T("apl_text_data V 0.a1\n"));
	file.WriteString(data.m_CurSchema);

	//if(data.m_CurSchema2!=_T("")) {file.WriteString(_T("+")); file.WriteString(data.m_CurSchema2);}

	for(i=0;i<data.m_CurAddSchemes.GetSize();i++)
	{
		file.WriteString(_T("+")); file.WriteString(data.m_CurAddSchemes.GetAt(i)->m_scheme);
	}

	buf.Format(_T("\n%i  - MaxId"), data.GetMaxInstNum());
	file.WriteString(buf);
	buf.Format(_T("\n%i  - Instances"), data.instances.Size);
	file.WriteString(buf);
	for(i=0;i<data.instances.Size;i++)
	{
		CaplInstance *inst_i=data.instances[i];
		if(0==inst_i->GetType()) continue;
		//if(inst_i->temporary && !save_temporary) continue;//16.01.2003
		
		buf.Format(_T("\n\n#%i=%s <%i,0x%p>"),i+1,LPCTSTR(inst_i->GetType()->name), inst_i->GetId(),inst_i);
		file.WriteString(buf);

		if(inst_i->attrs!=0)
		{
			CaplEntity *ent=inst_i->GetType();
			if(ent!=0)
			{
//				for(j=0;j<ent->all_attrs.Size;j++)
//				{
//					inst_i->attrs[j].value.Print(buf1,instmap,instmapsize);
//					buf.Format("\n   %s = %s",LPCTSTR(ent->all_attrs[j]->name),LPCTSTR(buf1));
//					file.WriteString(buf);
//				}
			}
		}
	}
//	if(instmap!=0) delete instmap;
	file.WriteString(_T("\n\nend"));
	file.Flush();
	file.Close();
	return true;
}
//************************************************************************
void DivideStringByPlus(const CString &str, CaplStringArray &sa)
{
	// sa.RemoveAll(); DIV: в массив sa надо добавлять дополнительные словари из разных источников
	if(str==_T("")) return;
	
	CString s;
	int i,j=0;
	while(true)
	{
		i=str.Find(_T('+'),j);
		if(i<0) s=str.Right(str.GetLength()-j);
		else {s=str.Mid(j,i-j); j=i+1;}

		s.TrimLeft(); s.TrimRight(); s.MakeLower();
		sa.Add(s);
		
		if(i<0)break;
	}
}
	
//************************************************************************


CaplInstance::CaplInstance(CaplStepData* data,CaplEntity *def_type, apl_id def_id, bool create_attrs)
{
	if(0==data)return;
	m_data=data;
	m_accessmode=aplOWN;
	m_id=def_id; m_type=def_type;
	m_temporary=false;
	m_is_user_access=false;
	m_access_pattern=0;
	if(def_type!=0 && create_attrs)
		attrs =new CaplValueDefinition[def_type->all_attrs.Size];
	else attrs=0;
	COleDateTime odt=COleDateTime::GetCurrentTime();
	m_dt_changed_attr=odt.m_dt;
	m_deleted = false;
	m_create_date = 0;
	m_create_user = NULL;
	m_update_date = 0;
	m_update_user = NULL;
}

CaplInstance::~CaplInstance()
{
	if(attrs!=0) delete[] attrs;
}

void CaplInstance::ClearAttrs()
{
	if(0==m_type) return;
	int k=m_type->all_attrs.Size;
	if(0==attrs) return;
	for(int i=0;i<k;i++)
		attrs[i].Clear();
}

bool CaplInstance::IsChanged() const
{
	if(m_temporary)return false;
	if(0==m_type)
	{
		if(m_id!=0 && m_deleted){return true;}
		return false;
	}
	if(0==attrs)return false;
	for(int j=0;j<m_type->all_attrs.Size;j++)
	{
		if(attrs[j].changed==true){return true;}
	}
	return false;
}

void CaplInstance::CaplValueDefinition::Clear()
{
	if(ArhiveAggr!=0) {delete ArhiveAggr; ArhiveAggr=0;}
	value.Clear();
	changed=false;

}

void CaplInstance::SetServiceParamFull(CaplEntity *type)
{
	if(m_type==type)
		return;
	if(m_type!=0)
	{
		for(int i=0;i<m_type->instances.Size;i++)
		{
			if(m_type->instances[i]==this){m_type->instances.Remove(i); break;}
		}
	}
	m_type=type;
	if(m_type!=0)
	{
		bool u=m_type->instances.Unique;
		m_type->instances.Unique=false;
		m_type->instances. Add(this);
		m_type->instances.Unique=u;
	}
}



//**********************************************************************
//**********************************************************************
//**********************************************************************
CaplStepData::CaplStepData()
{
	m_LastErrorCode=0;
	m_MaxInstNum=0;
	m_ErrorPrintMode=true;	
	m_ErrorMessageMode=false;
	m_WarningPrintMode=false;	
	m_WarningMessageMode=false;
	m_Default_TextFormat=false;
	m_TestAccessMode=true;
	m_MakeCopyAggr=false;
	m_DictionatyFolder=_T("");	
	m_CurSchemaVersion=0;
	//m_CurSchemaVersion2=0;
	m_ErrorAnnotation="";
	m_is_readonly=false;

	m_AttrIndex=0;
	m_AttrIndex2=0;
	m_EntityIndex=0; m_EntityIndex2=0;
	m_MaxEntId=0;
	m_MaxEntId2=0; m_MinEntId2=0;
	m_MaxAttrId=0;
	m_MaxAttrId2=0; m_MinAttrId2=0;
	m_CurDataFile=_T("");
	m_CurSchema = _T("");
	//m_CurSchema2 = _T("");

	m_admin_mode=false;
	m_check_val_in_putattr=false;

	m_SetErrorMode = true;
	m_ClearDataAfterError = true;

#ifdef _UNICODE
	// считаем, что для юникодного клиента основной юникодный сервер
	m_global_ansi_string = false;
#else
	// а для анси клиента - анси сервер
	m_global_ansi_string = true;
#endif
	m_auto_load_add_dict = true;

	m_bufer_for_err_message = 0;
	m_append_all_chemas = false;

	m_dwLastPaintTickCount=0;
	instances.AutoKillReference = true; // нужно после смены типа instances на aplExtent

#ifdef APL_TRANSLATE_ENABLED
#ifdef _MFC_VER
	if(-1==(int)CaplTranslate::GetGurLocale()) 	CaplTranslate::Init();
#else
	if(0==CaplTranslate::GetGurLocale()) 	CaplTranslate::Init();
#endif
#endif
	m_non_admin_readonly = false;

	SetGetStepDataErrorDescription(CaplStepData::GetErrorDescription);

};



void CaplStepData::PrintError(bool warning) const
{
#ifndef _MFC_VER
	if(m_MultyThreads)
	{
		SThreadContext* pErr = GetErrContext();
		if(pErr==0) return;
		if(!warning)
		{
			if(m_ErrorPrintMode)aplMessagePrint(pErr->m_ErrorDescription,MB_ICONERROR);
			if(m_ErrorMessageMode)aplMessageBox(pErr->m_ErrorDescription,MB_ICONERROR|MB_OK|MB_TASKMODAL);
			QT_THREAD_ENTER_CS
			if(m_bufer_for_err_message!=0){m_bufer_for_err_message->Append(_T("\n"));m_bufer_for_err_message->Append(pErr->m_ErrorDescription);}
			QT_THREAD_LEAVE_CS
		}
		else
		{
			if(m_WarningPrintMode)aplMessagePrint(pErr->m_ErrorDescription,MB_ICONWARNING);
			if(m_WarningMessageMode)aplMessageBox(pErr->m_ErrorDescription,MB_ICONWARNING|MB_OK|MB_TASKMODAL);
		}
		return;
	}
#endif
	if(!warning)
	{
		if(m_ErrorPrintMode)aplMessagePrint(m_ErrorDescription,MB_ICONERROR);
		if(m_ErrorMessageMode)aplMessageBox(m_ErrorDescription,MB_ICONERROR|MB_OK|MB_TASKMODAL);
		QT_THREAD_ENTER_CS
		if(m_bufer_for_err_message!=0){m_bufer_for_err_message->Append(_T("\n"));m_bufer_for_err_message->Append(m_ErrorDescription);}
		QT_THREAD_LEAVE_CS
	}
	else
	{
		if(m_WarningPrintMode)aplMessagePrint(m_ErrorDescription,MB_ICONWARNING);
		if(m_WarningMessageMode)aplMessageBox(m_ErrorDescription,MB_ICONWARNING|MB_OK|MB_TASKMODAL);
		//if(m_bufer_for_err_message!=0){m_bufer_for_err_message->Append("\n");m_bufer_for_err_message->Append(m_ErrorDescription);}
	}
}

CaplStepData::~CaplStepData()
{
	ClearDict();
};


bool CaplStepData::IsMyInstance(CaplInstance *inst,bool SafeFind)
{
	if(0==inst) return false;
	if(inst==(CaplInstance *)-1) return false;
	if(!SafeFind){
//		старый вариант
// 		if(inst->GetId()!=0 && instances.Sorted_id){
// 			// есть все условия для быстрого поиска делением пополам. Грех перебирать все
// 			return (-1!=aplQFindInstIdInExtent(*((aplExtent*)(&instances)),inst->GetId(),instances.GetSize(),false));
// 		}
		return inst->GetMyData()==this;
	}
	// раз режим защищенный - мы не знаем, насколько валидный указатель на инстанс 
	// нам пришел. Приходится передирать
	if(instances.Sorted_inst){
		// есть другие условия для быстрого поиска делением пополам. Грех перебирать все
		return (-1!=aplQFindInstInExtent(*((aplExtent*)(&instances)),inst,false));
	}
	for(int i=0;i<instances.Size;i++)
	{
		if(inst==instances[i]){
			if(0==inst->GetType()) return false;
			return true;
		}
	}
	return false;
}

bool CaplStepData::IsEntityAttr(CaplEntity *entity,CaplAttr *attr) const
{
	if(0==entity) return false;
	if(0==attr) return false;
	if(attr->entity==entity)return true;

	if(attr->index>=0)
	{
		if(entity->all_attrs.Size<attr->index) 
			return false;
		if(entity->all_attrs[attr->index]!=attr) 
			return false;
		return true;
	}

	for(int i=0;i<entity->all_supertypes.Size;i++)
	{
		if(attr->entity==entity->all_supertypes[i]){
			return true;
		}
	}
	return false;
}
//**********************************************************************
void CaplStepData::SetLastError(int ErrorCode, bool warning) const
{
#ifndef _MFC_VER
	if(m_MultyThreads)
	{
		SThreadContext* pErr = GetErrContext();
		if(pErr==0) return;
#ifndef _DEBUG
		if(m_SetErrorMode)
#else
		if(m_SetErrorMode && ErrorCode!=pErr->m_LastErrorCode)
#endif
		{
			pErr->m_LastErrorCode=ErrorCode;
			pErr->m_ErrorDescription=GetErrorDescription(ErrorCode);
			pErr->m_ErrorDescription+=pErr->m_ErrorAnnotation;

			if(ErrorCode!=APLAPIERR_NOERROR)
			{
				pErr->m_ErrorAnnotation=_T("");// чтобы аннотация не очищалась при SetLastError(0)
				PrintError(warning);
			}
		}
		return;
	}
#endif

#ifndef _DEBUG
	if(m_SetErrorMode)
#else
	if(m_SetErrorMode && ErrorCode!=m_LastErrorCode)
#endif
	{
		m_LastErrorCode=ErrorCode;
		m_ErrorDescription=GetErrorDescription(ErrorCode);
		m_ErrorDescription+=m_ErrorAnnotation;
	
		if(ErrorCode!=APLAPIERR_NOERROR)
		{
			m_ErrorAnnotation=_T("");// чтобы аннотация не очищалась при SetLastError(0)
			PrintError(warning);
		}
	}
}

#ifdef __linux__
	#define THREAD_FORMAT   _T("t%04i")
	#define LOG_SHOW_ERROR_FC(lpszFormat,...) do{CString csInternTextLSEFC(_T("")),str_tmp; csInternTextLSEFC.Format(lpszFormat, ##__VA_ARGS__); \
		csInternTextLSEFC += _T("\nFunction: "); csInternTextLSEFC += __APL_FUNC__; \
		aplMessagePrint(csInternTextLSEFC, MB_ICONERROR); }while(false)
#else
	#define THREAD_FORMAT   _T("t0x%04X")
#endif


void CaplStepData::SetLastErrorEx(int ErrorCode, bool warning, LPCTSTR file, int line, CaplInstance* inst, CaplAttr *attr, CaplEntity *entt, CaplEntity *entt2, LPCTSTR ExtText) const
{
	if(m_SetErrorMode)
	{
#ifndef _MFC_VER
		SThreadContext* pErr=0;
#endif
		CString buf, ErrorDescription;
		ErrorDescription=GetErrorDescription(ErrorCode)+CString(_T("!\n\n"));
#ifdef _MFC_VER
		ErrorDescription+=m_ErrorAnnotation;
#else
		if(m_MultyThreads)
		{
			pErr = GetErrContext();
			if(pErr==0) return;
			ErrorDescription+=pErr->m_ErrorAnnotation;
			pErr->m_ErrorAnnotation.Empty();
		}
		else
			ErrorDescription=GetErrorDescription(ErrorCode)+CString(_T("!\n\n"))+m_ErrorAnnotation;
#endif
		if(0==inst) ErrorDescription+=_T("\n Instance==0");
		else if(inst==(CaplInstance*)-1) ErrorDescription+=_T("\n Instance==-1");
		else if(inst!=(CaplInstance*)-2) 
		{
			buf.Format(_T("\n Instance id: %i "),inst->GetId());
			ErrorDescription+=buf;
			if(0==inst->GetType()){buf=_T("type=0");}
			else{buf=_T("type=")+inst->GetType()->name;}
			ErrorDescription+=buf;
		}

		if(0==attr) ErrorDescription+=_T("\n Attr==0");
		else if(attr==(CaplAttr *)-1) ErrorDescription+=_T("\n Attr==-1");
		else if(attr!=(CaplAttr *)-2)
		{
			buf.Format(_T("\n Attr id: %i "),attr->id);
			ErrorDescription+=buf;
			ErrorDescription+=_T(" name= ");
			ErrorDescription+=attr->name;
		}
		if(0==entt) ErrorDescription+=_T("\n Entt==0");
		else if(entt==(CaplEntity *)-1) ErrorDescription+=_T("\n Entt==-1");
		else if(entt!=(CaplEntity *)-2)
		{
			buf.Format(_T("\n Entity id: %i "),entt->id);
			ErrorDescription+=buf;
			ErrorDescription+=_T(" name= ");
			ErrorDescription+=entt->name;
		}
		if(0==entt2) ErrorDescription+=_T("\n Entt2==0");
		else if(entt2==(CaplEntity *)-1) ErrorDescription+=_T("\n Entt2==-1");
		else if(entt2!=(CaplEntity *)-2)
		{
			buf.Format(_T("\n Entity 2 id: %i "),entt2->id);
			ErrorDescription+=buf;
			ErrorDescription+=_T(" name= ");
			ErrorDescription+=entt2->name;
		}
		if(!aplIsStringEmpty(ExtText))
		{
			ErrorDescription += _T(" Extend Info:  ");
			ErrorDescription += ExtText;
		}

		if(!aplIsStringEmpty(file) && line !=0 && ((m_WarningPrintMode && warning) || (m_ErrorPrintMode && !warning)))
		{
			buf.Format(_T("%s(%i) : {%i} %s (Thread ") THREAD_FORMAT _T(")"), LPCTSTR(file), line, ErrorCode, warning?_T("Warning "):_T("Error "), GetCurrentThreadId());
			ErrorDescription+=_T("\n Src: ")+buf;
		}

#ifdef _MFC_VER
		m_LastErrorCode=ErrorCode;
		m_ErrorDescription=ErrorDescription;
		m_ErrorAnnotation=_T("");
#else
		if(m_MultyThreads)
		{
			if(pErr==0) return;
			pErr->m_LastErrorCode=ErrorCode;
			pErr->m_ErrorDescription=ErrorDescription;
			pErr->m_ErrorAnnotation=_T("");
		}
		else
		{
			m_LastErrorCode=ErrorCode;
			m_ErrorDescription=ErrorDescription;
			m_ErrorAnnotation=_T("");
		}

#endif

		if(ErrorCode!=APLAPIERR_NOERROR) 
			PrintError(warning);
	}
}

void CaplStepData::SetLastErrorEx(SaplErrorDescription &error)
{
#ifndef _MFC_VER
	if(m_MultyThreads)
	{
		SThreadContext* pErr = GetErrContext();
		if(pErr==0) return;
#ifndef _DEBUG
		if(m_SetErrorMode)
#else
		if(m_SetErrorMode && error.m_err_code!=pErr->m_LastErrorCode)
#endif
		{
			pErr->m_LastErrorCode=error.m_err_code;
			pErr->m_ErrorDescription=error.Print(pErr->m_ErrorAnnotation.IsEmpty()?(TCHAR*)0:pErr->m_ErrorAnnotation);
			pErr->m_ErrorDescription+=pErr->m_ErrorAnnotation;

			if(error.m_err_code!=APLAPIERR_NOERROR)
			{
				pErr->m_ErrorAnnotation=_T("");// чтобы аннотация не очищалась при SetLastError(0)
				PrintError(false);
			}
		}
		return;
	}
#endif

#ifndef _DEBUG
	if(m_SetErrorMode)
#else
	if(m_SetErrorMode && error.m_err_code!=m_LastErrorCode)
#endif
	{
		m_LastErrorCode=error.m_err_code;
		m_ErrorDescription=error.Print(m_ErrorAnnotation.IsEmpty()?(TCHAR*)0:m_ErrorAnnotation);
		m_ErrorAnnotation=_T("");

		if(error.m_err_code!=APLAPIERR_NOERROR)
			PrintError(false);
	}
}

CString CaplStepData::GetErrorDescription(int ErrorCode )
{
	switch (ErrorCode)
	{
/*		case APLAPIERR_NOERROR: m_ErrorDescription="Нет ошибки";break;
		case APLAPIERR_BADINPUTDATA: m_ErrorDescription= "Кривые входные данные";break;
		case APLAPIERR_NODATA: m_ErrorDescription= "Нет данных";break;
		case APLAPIERR_BADTYPE: m_ErrorDescription= "Неверный тип данных";break;
		case APLAPIERR_FILE_IO: m_ErrorDescription= "Ошибка файлового ввода/вывода";
		case APLAPIERR_BADDICTIONARY: m_ErrorDescription="Корявый словарик";break;
		case APLAPIERR_BADENT: m_ErrorDescription="Корявая Entity";break;
		case APLAPIERR_BADATTR: m_ErrorDescription="Корявый атрибут";break;
		case APLAPIERR_BADDATA: m_ErrorDescription="Корявые данные";break;
		case APLAPIERR_ATTRNSET: m_ErrorDescription="Атрибут не установлен";break;
	//	case : m_ErrorDescription="";break;
		default: m_ErrorDescription= "Неизвестная ошибка";break;

*/
// 		case APLAPIERR_NOERROR: return "No Error";break;
 		case APLAPIERR_NOERROR: return APL_T("Операция завершена успешно");break;
// //		case APLAPIERR_BADINPUTDATA: m_ErrorDescription= "Invalid input data";break;
 		case APLAPIERR_NODATA: return APL_T("No data");break;
// 		case APLAPIERR_BADTYPE: return "Invalid data type";break;
 		case APLAPIERR_BADTYPE: return APL_T("Неверный тип инстансы или тип атрибута");break;
// 		case APLAPIERR_FILE_IO: return "Error file I/O";break;
 		case APLAPIERR_FILE_IO: return APL_T("Ошибка чтения или записи файла");break;
// 		case APLAPIERR_BADDICTIONARY: return"Invalid dictionary";break;
		case APLAPIERR_BADDICTIONARY: return APL_T("Некорректный файл словаря");break;
// 		case APLAPIERR_BADENT: return "Invalid entity";break;
 		case APLAPIERR_BADENT: return APL_T("Неверная или пустая entity");break;
// 		case APLAPIERR_BADATTR: return"Invalid attribute";break;
 		case APLAPIERR_BADATTR: return APL_T("Неверный или пустой объект атрибута");break;
// 		case APLAPIERR_BADDATA: return"Invalid data";break;
		case APLAPIERR_BADDATA: return APL_T("Неверные данные в буфере или в БД");break;
// 		case APLAPIERR_ATTRNSET: return"Attribute not set";break;
 		case APLAPIERR_ATTRNSET: return APL_T("Атрибут не установлен");break;
// 		case APLAPIERR_BADFILENAME: return"Bad File Name";break;
 		case APLAPIERR_BADFILENAME: return APL_T("Неверное имя файла");break;
// 		case APLAPIERR_BADINSTANCE: return"Bad Instance";break;
 		case APLAPIERR_BADINSTANCE: return APL_T("Неверный или пустой Instance");break;
// 		case APLAPIERR_BADINSTANCETYPE: return "\nBad Instance type";break;
 		case APLAPIERR_BADINSTANCETYPE: return APL_T("Неверный тип Instance");break;
// 		case APLAPIERR_BADSCHEMANAME: return "Bad schema name";break;
 		case APLAPIERR_BADSCHEMANAME: return APL_T("Неверное имя словаря");break;
// 		case APLAPIERR_NOACCESSRIGHT: return "No Access Right";break;
 		case APLAPIERR_NOACCESSRIGHT: return APL_T("Недостаточно прав доступа на объект");break;
// 		case APLAPIERR_BADDICTVERSION: return "Invalid dictionary version";break;
 		case APLAPIERR_BADDICTVERSION: return APL_T("Неподдерживаемый формат словаря");break;
// 		case APLAPIERR_INVALIDDATAFORMAT: return "Invalid data format";break;
 		case APLAPIERR_INVALIDDATAFORMAT: return APL_T("Неподдерживаемый формат данных");break;
// 		case APLAPIERR_INCOMPATIBLEDICT: return "Uncompatible dictionary";break;
 		case APLAPIERR_INCOMPATIBLEDICT: return APL_T("Невозможно сменить словарь");break;
// 		case APLAPIERR_FICTIVE_DATA: return "Can't use with Fictive data";break;
 		case APLAPIERR_FICTIVE_DATA: return APL_T("Операция недоступна в виртульном ядре Fictive data");break;
// 		case APLAPIERR_DICT_FILE_NOT_OPEN: return "Error load dictionary file";break;
 		case APLAPIERR_DICT_FILE_NOT_OPEN: return APL_T("Ошибка чтения файла словаря");break;
// 		case APLAPIERR_BADDATA_INVALID_ENTINY_ID:return "Invalid entity ID in file";break;
 		case APLAPIERR_BADDATA_INVALID_ENTINY_ID:return APL_T("В файле данных встречен объект с типом (entity), которого нет в текущем словаре");break;
// 		case APLAPIERR_BADDATA_INVALID_ATTR_ID:return "Error parse: invalid attr ID ";break;
 		case APLAPIERR_BADDATA_INVALID_ATTR_ID:return APL_T("В файле данных встречен объект с атрибутом, которого нет в текущем словаре");break;
// 		case APLAPIERR_FILE_BD_NOT_FOUND:return "File BD not found";break;
 		case APLAPIERR_FILE_BD_NOT_FOUND:return APL_T("Файл БД не найден");break;
// 		case APLAPIERR_FILE_BD_READ_ONLY:return "Files of BD has readonly attribute";break;
		case APLAPIERR_MODE_READ_ONLY:return  APL_T("Система работает в режиме 'только чтение'");break;
		case APLAPIERR_FILE_BD_READ_ONLY:return  APL_T("Файл БД доступен только для чтения");break;
		case APLAPIERR_INCOMPATIBLDATAFORMAT:return  APL_T("Версия файла данных не поддерживается. Обновите клиента и сервер приложений.");break;
		case APLAPIERR_OUT_OF_MEMORY:return  APL_T("Ошибка при выделении памяти.");break;
		case APLAPIERR_UNKNOWN_EXEPTION:return  APL_T("Неизвестное исключение.");break;
		case APLAPIERR_UNCORRECT_STATE:return  APL_T("Режим ядра не сответствует функции.");break;
		default: return  _T("Unknown error");break;			
	}
}

#ifndef _MFC_VER
/// Вернуть код текущей ошибки
int CaplStepData::GetLastError()
{
	if(m_MultyThreads)
	{
		SThreadContext* pErr = GetErrContext();
		if(pErr==0) return APLAPIERR_NOERROR;
		return pErr->m_LastErrorCode;
	}
	return m_LastErrorCode;
}

/** Вернуть текстовое описание текущей ошибки*/
LPCTSTR CaplStepData::GetLastErrorDescription() const
{
	if(m_MultyThreads)
	{
		SThreadContext* pErr = GetErrContext();
		if(pErr==0) return APLAPIERR_NOERROR;
		return pErr->m_ErrorDescription;
	}
	return LPCTSTR(m_ErrorDescription);
}

void CaplStepData::SetErrorAnnotation(LPCTSTR annotation)
{
	if(m_MultyThreads)
	{
		SThreadContext* pErr = GetErrContext();
		if(pErr==0) return;
		pErr->m_ErrorAnnotation = annotation;
		return;
	}
	m_ErrorAnnotation = annotation;
}
void CaplStepData::SetErrorDescription(LPCTSTR description)
{
	if(m_MultyThreads)
	{
		SThreadContext* pErr = GetErrContext();
		if(pErr==0) return;
		pErr->m_ErrorDescription = description;
		return;
	}
	m_ErrorDescription = description;
}
#endif


//************************************************************
void CaplStepData::SetDictionaryFolder(LPCTSTR folder)
{
	if(!aplIsStringEmpty(folder))
	{
		m_DictionatyFolder = folder;
		if(m_DictionatyFolder != _T(""))
		{
			if(m_DictionatyFolder[m_DictionatyFolder.GetLength()-1]!=aplDirRazd) m_DictionatyFolder+=aplDirRazd;
		}
	}
	else m_DictionatyFolder = _T("");
}
//**********************************************************************
bool CaplStepData::LoadDictionary(LPCTSTR schema)
{
	QT_THREADS_DENY_FROM_READ
	if(aplIsStringEmpty(schema)) {SetLastErrorWithFileInfo(APLAPIERR_BADSCHEMANAME);return false;}
	SetLastError(0);
	ClearDict();
	CString sSchema=schema; sSchema.MakeLower();
	CString buf, dic_file_name;
	CaplStringArray saShemes;

	dic_file_name=m_DictionatyFolder;
	dic_file_name+=sSchema;
	dic_file_name+=_T(".dict");

	bool b=LoadDictionaryFromFile(LPCTSTR(dic_file_name));
	if(!b) return b;

	// используется в BDAdminOracle - чтобы догрузка работала для словаря с нестандартным названием
	if(!m_append_all_chemas && sSchema!=_T("apl_pss_a")) return true;

#ifndef _MFC_VER
	/* Все дополнительные словари задаются параметром ini. Оставил старый код для примера.
    if(sSchema==_T("apl_pss_a"))  // Пока принудительно догружаем схему ils для РФЯЦ
    {
		saShemes.Add(_T("apl_ils_a"));
    }
	*/

#else

	// ищем в реестре настройку дополнительного словаря
	HKEY hRegKey=0;
	// сначала ищем в 64-битной ветке реестра
	if(ERROR_SUCCESS!=RegOpenKeyEx(HKEY_LOCAL_MACHINE,_T("SOFTWARE\\CALS Centre \"Applied Logistic\""),0, KEY_WOW64_64KEY | KEY_QUERY_VALUE,&hRegKey))
	{
		// если не нашли 64-битную ветку - ищем в 32-битной
		if(ERROR_SUCCESS!=RegOpenKeyEx(HKEY_LOCAL_MACHINE,_T("SOFTWARE\\CALS Centre \"Applied Logistic\""),0,KEY_QUERY_VALUE,&hRegKey))return true;
	}
	if(0==hRegKey) return true;

	buf=_T("");
	DWORD dwType= REG_SZ, dwSize;
	if(ERROR_SUCCESS==RegQueryValueEx(hRegKey, _T("apl_pss_a_addition"), 0, &dwType, 0, &dwSize) && 0!=dwSize)
	{
		TCHAR *pbValue= new TCHAR[dwSize/sizeof(TCHAR) +1];
		if(ERROR_SUCCESS==RegQueryValueEx(hRegKey, _T("apl_pss_a_addition"), 0, &dwType, (LPBYTE)pbValue, &dwSize))
		{
			buf=(TCHAR*)pbValue;
		}
		delete pbValue;
	}
	RegCloseKey(hRegKey);

	buf.TrimLeft(); buf.TrimRight();

	if(buf!=_T(""))
		DivideStringByPlus(buf,saShemes); // Разбираем строчку на части
	else if(m_ListAddSchemes.IsEmpty())
		return true;

#endif

	DivideStringByPlus(m_ListAddSchemes, saShemes); // Добавляем в список дополнительные словари, заданные в ini

	int i,j;
	// Загружаем доп. словари
	for(i=0;i<saShemes.GetSize();i++)
	{
		CString sSheme_i=saShemes[i];

		// Проверяем, а не загружена ли уже эта схема
		if(sSheme_i == m_CurSchema) continue;
		bool bIsLoad=false;
		for(j=0;j<m_CurAddSchemes.GetSize();j++)
		{
			if(sSheme_i==m_CurAddSchemes.GetAt(j)->m_scheme) {bIsLoad=true; break;}
		}
		if(bIsLoad) continue;

		dic_file_name=m_DictionatyFolder;
		dic_file_name+=sSheme_i;
		dic_file_name+=_T(".dict");

		b=LoadDictionaryFromFile(LPCTSTR(dic_file_name),true,true);

		if(!b) return false;
	}
	return true;
}

bool CaplStepData::LoadDictionaryAddition(int id)
{
	QT_THREADS_DENY_FROM_READ
	if(m_CurSchema!=_T("apl_pss_a")) return false;

	//AfxMessageBox("Загружаем дополнительный словарь!");

	CString dic_file_name;
	dic_file_name=m_DictionatyFolder;

    if(id>=41000 && id<42000) // Диапазон ВНИИС
	{
		dic_file_name+=_T("apl_pss_a_vniis");
	}
    else if(id>=50000 && id<54000) // Диапазон TGB
    {
        dic_file_name+=_T("apl_tgb_a");
    }
	else if(id>=55000 && id<57000) // Диапазон прагматики
	{
		dic_file_name+=_T("apl_pragma_a");
	}
	else if(id>=57000 && id<58000) // Диапазон матрешки
	{
		dic_file_name+=_T("matr");
	}
	else if(id>=60000 && id<65000) // Диапазон ils для РФЯЦ
    {
        dic_file_name+=_T("apl_ils_a");
    }
	else if(!m_auto_load_add_dict)
	{
		CString str; str.Format(APL_T("Неизвестный словарь с entity_id %i"), id);
		APL_THROW_EX(APLAPIERR_BADDICTIONARY, str);
	}
	else return false;

    dic_file_name+=_T(".dict");
	
	if(!m_auto_load_add_dict)
		APL_THROW_EX(APLAPIERR_BADDICTIONARY, dic_file_name);

    return LoadDictionaryFromFile(dic_file_name,true,true);
}

//**********************************************************************
bool CaplStepData::LoadDictionaryAddition(CaplStrMap &dicts)
{
	if(!m_CurSchema.IsEmpty())
	{
		int indx = dicts.Find(m_CurSchema);
		if(indx!=-1) dicts.Remove(indx);
		
		for(int i=0; i<m_CurAddSchemes.GetSize(); i++)
		{
			CaplStepData::CAddScheme *addscheme=m_CurAddSchemes.GetAt(i);
			if(0==addscheme) continue;
			indx = dicts.Find(addscheme->m_scheme);
			if(indx!=-1) dicts.Remove(indx);
		}
	}
	for(int i=0; i<dicts.GetSize(); i++)
	{
		if(!LoadDictionaryFromFile(m_DictionatyFolder + dicts.GetAt(i)->str + _T(".dict"), true, true)){return false; }
	}
	return true;
}



//**********************************************************************

CString CaplStepData::GetDictionaryDescription()
{
	CString buf, sDictDescr;

    sDictDescr.Format( _T("%s v%i"),LPCTSTR(m_CurSchema),m_CurSchemaVersion);

	for(int i=0; i<m_CurAddSchemes.GetSize(); i++)
	{
		CaplStepData::CAddScheme *addscheme=m_CurAddSchemes.GetAt(i);
		if(0==addscheme) continue;
		
        buf.Format( _T(" + %s v%i"),LPCTSTR(addscheme->m_scheme),addscheme->m_version);

		sDictDescr+=buf;
	}
	return sDictDescr;
}

//**********************************************************************

bool CaplStepData::GetDictionaryDescription(CaplStrMap &map)
{
	CString buf, sDictDescr;
	map.Clear();
    map.Add(m_CurSchema, m_CurSchemaVersion);

	for(int i=0; i<m_CurAddSchemes.GetSize(); i++)
	{
		CaplStepData::CAddScheme *addscheme=m_CurAddSchemes.GetAt(i);
		if(0==addscheme) continue;
		map.Add(addscheme->m_scheme, addscheme->m_version);
	}
	return true;
}
//**********************************************************************
bool CaplStepData::SaveDictionaryToString(CString &sString)
{
	sString=_T("");
	sString.GetBuffer(256*1024); sString.ReleaseBuffer();

	int i,j;
	CString buf,buf1,buf2;
	/*if(m_CurSchema2==_T("")) 
		buf.Format(_T("APL DICTIONARY FILE V 2.0\n%s\nver %i"), LPCTSTR(m_CurSchema),m_CurSchemaVersion);
	else 
		buf.Format(_T("APL DICTIONARY FILE V 2.0\n%s+%s\nver %i+%i"), LPCTSTR(m_CurSchema), LPCTSTR(m_CurSchema2),m_CurSchemaVersion,m_CurSchemaVersion2);
	*/

	if(0==m_CurAddSchemes.GetSize()) 
		buf.Format(_T("APL DICTIONARY FILE V 2.0\n%s\nver %i"), LPCTSTR(m_CurSchema),m_CurSchemaVersion);
	else
	{
		buf=_T("APL DICTIONARY FILE V 2.0\n"); buf+=m_CurSchema;
		buf1.Format(_T("\nver %i"), m_CurSchemaVersion);

		for(i=0;i<m_CurAddSchemes.GetSize();i++)
		{
			buf+=_T("+"); buf+=m_CurAddSchemes.GetAt(i)->m_scheme;
			buf2.Format(_T("+%i"),m_CurAddSchemes.GetAt(i)->m_version); 
			buf1+=buf2;
		}
		buf+=buf1;
	}
	

	sString+=buf;
	for(i=0;i<entities.Size;i++)
	{
		CaplEntity *ent=entities[i];
		TCHAR complex=_T('N');
		if(ent->complex)complex=_T('Y');
		buf.Format(_T("\nE %i %s %c"),ent->id,LPCTSTR(ent->name),complex);
		for(j=0;j<ent->supertypes.Size;j++)
		{
			buf1.Format(_T(" %i"),ent->supertypes[j]->id);
			buf+=buf1;
		}
		sString+=buf;
	}

	for(i=0;i<attrs.Size;i++)
	{
		CaplAttr *attr=attrs[i];
		TCHAR opt=_T('F');
		if(attr->optional) opt=_T('T');
		int redeclar=0;
		if(attr->redeclaring!=0)redeclar=attr->redeclaring->id;
		buf.Format(_T("\nA %i %c %3i %c %3i %s "),attr->id,attr->vid, attr->entity->id, opt,redeclar,
			LPCTSTR(attr->name));
		sString+=buf;
		aplValue2String(attr->type,buf); sString+=buf;
		for(j=0;j<8;j++)
		{
			if(attr->add_types[j]==aplNOTYPE) break;
			aplValue2String(attr->add_types[j],buf);
			sString+=_T(" "); sString+=buf;
		}
		if(attr->inst_type!=0)
		{
			buf.Format(_T(" %i"),attr->inst_type->id);
			sString+=buf;
		}
	}
	return true;
}
//**********************************************************************
bool CaplStepData::SaveDictionaryToMemory(CaplDataBuf &dbuf)
{
	dbuf.Clear();
	// словарь передается исключительно в ansi виде
	dbuf.m_ansi_string = true;
	CString buf;
	if(!SaveDictionaryToString(buf)) return false;
	dbuf.AddStr(buf);
	return true;
}
//**********************************************************************
bool CaplStepData::SaveDictionaryToFile(LPCTSTR file)
{
	if(aplIsStringEmpty(file)) return false;

	/*CaplDataBuf dbuf;
	SaveDictionaryToMemory(dbuf);
	if(0==dbuf.m_Size) return false;

	CStdioFile outf;
	if(TRUE!=outf.Open(file,CFile::modeCreate|CFile::modeWrite|CFile::typeText)) return false;
	
	outf.Write(dbuf.m_data,dbuf.m_Size);

	outf.Flush();
	outf.Close();*/

	CString sDict;
	if(!SaveDictionaryToString(sDict)) return false;
	CaplStringFile str_file;
	if(!str_file.Open(file, CaplFile::modeCreate|CaplFile::modeWrite)) return false;
	str_file.SetFileEncoding(aplANSI);
	str_file.WriteString(sDict);
	BOOL b=str_file.Flush();
	str_file.Close();

	return b ? true : false;
}
//**********************************************************************
//**********************************************************************
//**********************************************************************

// добавил Дещере 27_09_01
int compare_aplEntity_id( const void *arg1, const void *arg2 )
{
	if(0==arg1) return -1;
	if(0==arg2) return  1;
	if((*(CaplEntity **)arg1)->id < (*(CaplEntity **)arg2)->id) return -1;
	if((*(CaplEntity **)arg1)->id > (*(CaplEntity **)arg2)->id) return 1;
	return 0;
}
// добавил Дещере 27_09_01
int compare_aplAttr_id( const void *arg1, const void *arg2 )
{
	if(0==arg1) return -1;
	if(0==arg2) return  1;
	if((*(CaplAttr **)arg1)->id < (*(CaplAttr **)arg2)->id) return -1;
	if((*(CaplAttr **)arg1)->id > (*(CaplAttr **)arg2)->id) return 1;
	return 0;
}

// добавил Дещере 05_03_10
bool CaplStepData::LoadDictionaryFromMemory(CaplDataBuf &dict_dbuf, bool bApend)
{
	QT_THREADS_DENY_FROM_READ
	CaplStackLogger stack_logger(_T("CaplStepData::LoadDictionaryFromMemory"));

	if(0==dict_dbuf.m_Size) {SetLastErrorWithFileInfo(APLAPIERR_BADSCHEMANAME);return false;}
	
	dict_dbuf.Reset();
	// для совместимости словарь оставляем в ansi
	//dict_dbuf.m_ansi_string = m_global_ansi_string;
	dict_dbuf.m_ansi_string = true;

	SetLastError(0);
	CString buf, word, name, s_id,s_ent,s_type;
	apl_id id, l_ent;
	int i,j,k;
	aplValueType attr_type;

	if(!bApend)ClearDict();
	
	CaplMap map_attrs, map_ent_ids;
	map_attrs.bAutoSort = true;
	map_ent_ids.bAutoSort = true;
	CaplStrMap map_ent_names;
	entities.Unique=false;
	attrs.Unique=false;

	if(bApend)
	{
		// запоминаем существующее
		for(i=0;i<entities.GetSize();i++)
		{
			CaplEntity *ent=entities[i];
			map_ent_ids.Add(ent->id,ent);
			map_ent_names.Add(ent->name,ent);
		}

		for(i=0;i<attrs.GetSize();i++)
		{
			CaplAttr *attr=attrs[i];
			map_attrs.Add(attr->id,attr);
		}
	}


	int dict_error_line=0;
	try
	{
		//чтение заголовка
		dict_dbuf.ReadString(buf);
		if(buf!=_T("APL DICTIONARY FILE V 2.0")) {APL_THROW(APLAPIERR_BADDICTVERSION); }
		//чтение имени схемы
		dict_dbuf.ReadString(buf);
		//if(buf!=schema) {APL_THROW(APLAPIERR_BADDICTIONARY);}
		
		// Разбираем название схемы на части
		i=buf.Find(_T('+'));
		if(i<=0)
		{
			if(!bApend || m_CurSchema==_T("")) m_CurSchema=buf;
			//else m_CurSchema2=buf; // так было когда доп словарь был один
			else m_CurAddSchemes.Add( new CAddScheme(buf,-1));
		}
		else
		{
			/* так было когда доп словарь был один
			if(m_CurSchema==_T(""))
			{
				CString buf1=buf.Left(i); m_CurSchema=buf1;
				buf1=i+1+LPCTSTR(buf); m_CurSchema2=buf1;
			}
			else m_CurSchema=buf;*/

			CaplStringArray sa;
			DivideStringByPlus(buf,sa);
			if(m_CurSchema==_T(""))
			{
				m_CurSchema=sa[0];
				for(int j=1; j<sa.GetCount(); j++) m_CurAddSchemes.Add( new CAddScheme(sa[j],0));
			}
			else m_CurSchema=buf;  // ЯАИ:  Я не понял смысла, но оставил как было.  По идее такого случая быть не должно
		}


		if(!dict_dbuf.ReadString(buf)) {APL_THROW_EX(APLAPIERR_BADDICTIONARY,_T("Error reading version")); }
		i=0;
		if(!aplGetWord(LPCTSTR(buf),i,word)){APL_THROW_EX(APLAPIERR_BADDICTIONARY,_T("Error reading version"));}
		if(word!=_T("ver")){APL_THROW_EX(APLAPIERR_BADDICTIONARY,_T("Missed format version's string"));}
		if(!aplGetWord(LPCTSTR(buf),i,word)){APL_THROW_EX(APLAPIERR_BADDICTIONARY,_T("Error reading version"));}
		
		//if(!bApend) m_CurSchemaVersion=atoi(LPCTSTR(word));

		i=word.Find(_T('+'));
		if(i<=0)
		{
			if(!bApend || 0==m_CurSchemaVersion)  m_CurSchemaVersion=_atoi(LPCTSTR(word));
			//else m_CurSchemaVersion2=_atoi(LPCTSTR(word)); // так было когда доп словарь был один
			else
			{
				// Задаем последний
				int k=m_CurAddSchemes.GetSize()-1; 
				if(k>=0) m_CurAddSchemes.GetAt(k)->m_version=_atoi(LPCTSTR(word));
			}
		}
		else
		{
			/* так было когда доп словарь был один
			if(0==m_CurSchemaVersion)
			{
				CString buf1=word.Left(i); m_CurSchemaVersion=_atoi(buf1);
				buf1=i+1+LPCTSTR(word); m_CurSchemaVersion2=_atoi(buf1);
			}
			else m_CurSchemaVersion2=_atoi(buf); // теряем вторую часть, но такого быть не должно
			*/

			CaplStringArray sa;
			DivideStringByPlus(word,sa);

			if(0==m_CurSchemaVersion)
			{
				m_CurSchemaVersion=_atoi(sa[0]);
				for(int j=1; j<sa.GetCount(); j++) m_CurAddSchemes.GetAt(j-1)->m_version=_atoi(sa[j]);
			}
			//else m_CurSchemaVersion2=_atoi(buf); / ЯАИ:  По идее такого случая быть не должно - см. выше
		}

		dict_error_line=3;// в файлах строки принято нумеровать не с 0 - а с 1 - так все редакторы делают (//DIV)
		while(dict_dbuf.ReadString(buf))
		{
			dict_error_line++;
			i=0;
			if(buf == _T("")) continue;
			if(!aplGetWord(LPCTSTR(buf),i,word))APL_THROW_EX(APLAPIERR_BADDICTIONARY,_T("Missed format of string"));
			if(word[0]==_T(';')) continue;
			if(word[0]==_T('e'))
			{
				if(!aplGetWord(LPCTSTR(buf),i,s_id))APL_THROW_EX(APLAPIERR_BADDICTIONARY,_T("Error reading entity id"));
				id=__atol((LPCTSTR)s_id);
				if(!aplGetWord(LPCTSTR(buf),i,name))APL_THROW_EX(APLAPIERR_BADDICTIONARY,_T("Error reading entity name"));
				if(!aplGetWord(LPCTSTR(buf),i,word))APL_THROW_EX(APLAPIERR_BADDICTIONARY,_T("Error reading entity type"));
				
				if(0==id)APL_THROW_EX(APLAPIERR_BADDICTIONARY,_T("Missed entity id"));
				if(name == _T(""))APL_THROW_EX(APLAPIERR_BADDICTIONARY,_T("Missed entity name"));

				CaplEntity *ent_by_id=(CaplEntity *)map_ent_ids.QGetPointerByIn(id);
  				if(ent_by_id!=0)
				{
					CString str;
					if(!bApend)
					{
						str.Format(_T("Redefinition entity id: %i"),id);
						APL_THROW_EX(APLAPIERR_BADDICTIONARY,str);
					}
					if(name!=ent_by_id->name)
					{
						str.Format(APL_T("В догружаемом словаре есть entity с существующим ID %i, но другим именем!"),id);
						APL_THROW_EX(APLAPIERR_BADDICTIONARY,str);
					}
				}
  				CaplEntity *ent_by_name=(CaplEntity *)map_ent_names.GetP(name);
				if(ent_by_name!=0)
				{
					CString str;
					if(!bApend)
					{
						str.Format(_T("Redefinition entity name '%s'"),LPCTSTR(name));
						APL_THROW_EX(APLAPIERR_BADDICTIONARY,str);
					}
					if(id!=ent_by_name->id)
					{
						str.Format(APL_T("В догружаемом словаре есть entity с существующим именем '%s' и другим ID!"),LPCTSTR(name));
						APL_THROW_EX(APLAPIERR_BADDICTIONARY,str);
					}
				}
 
				if(0==ent_by_id)
				{
					CaplEntity *ent= new CaplEntity(id,name);
					if(word==_T("y")) ent->complex=true;
					entities.Add(ent);

					map_ent_ids.Add(id,ent);
					map_ent_names.Add(name,ent);

					// чтение супертипов
					while(aplGetWord(LPCTSTR(buf),i,word))
					{
						id=__atol((LPCTSTR)word);
						ent->supertypes_id.Add(id);
					}
				}
			}
			else if(word[0]==_T('a'))
			{
				if(!aplGetWord(LPCTSTR(buf),i,s_id))
					APL_THROW_EX(APLAPIERR_BADDICTIONARY,_T("Error reading attr id"));
				id=__atol((LPCTSTR)s_id);
				if(0==id) 	
					APL_THROW_EX(APLAPIERR_BADDICTIONARY,_T("Missed attr id"));
				if(!aplGetWord(LPCTSTR(buf),i,word))
					APL_THROW_EX(APLAPIERR_BADDICTIONARY,_T("Error reading attr's vid"));
				TCHAR vid=word[0];
				if(!aplGetWord(LPCTSTR(buf),i,word))
					APL_THROW_EX(APLAPIERR_BADDICTIONARY,_T("Error reading attr's entity id"));
				l_ent=__atol((LPCTSTR)word);
				if(0==l_ent) 
					APL_THROW_EX(APLAPIERR_BADDICTIONARY,_T("Missed attr's entity id"));
				if(!aplGetWord(LPCTSTR(buf),i,word))
					APL_THROW_EX(APLAPIERR_BADDICTIONARY,_T("Error reading attr's opt"));
				bool opt=false;
				if(word==_T("t")) opt=true;

				if(!aplGetWord(LPCTSTR(buf),i,word))
					APL_THROW_EX(APLAPIERR_BADDICTIONARY,_T("Error reading attr's redeclarar"));
				int l_redeclar=__atol((LPCTSTR)word);

				if(!aplGetWord(LPCTSTR(buf),i,name))
					APL_THROW_EX(APLAPIERR_BADDICTIONARY,_T("Error reading attr's name"));
				if(name == _T(""))
					APL_THROW_EX(APLAPIERR_BADDICTIONARY,_T("Missed attr's name"));
				if(!aplGetWord(LPCTSTR(buf),i,s_type))
					APL_THROW_EX(APLAPIERR_BADDICTIONARY,_T("Error reading attr's type"));
				if(s_type == _T(""))
					APL_THROW_EX(APLAPIERR_BADDICTIONARY,_T("Missed attr's type"));

				attr_type=String2aplValueType(LPCTSTR(s_type));
				if(attr_type==aplNOTYPE) 
					APL_THROW_EX(1,_T("Missed attr's type"));

				bool bReplaceAttr=false;
				CaplAttr *prev_attr=(CaplAttr *)map_attrs.QGetPointerByIn(id);
				if(prev_attr!=0)
				{
#ifndef ALLOW_COPY_ATTR
					APL_THROW_EX(APLAPIERR_BADDICTIONARY,_T("Redefinition attr id"));
#else
					if(prev_attr->name!=name ||
						prev_attr->entity_id!=l_ent ||
						prev_attr->type!=attr_type)
					{
						CString str;
						str.Format(_T("Redefinition attr id: %i"),id);
						APL_THROW_EX(APLAPIERR_BADDICTIONARY,str);
					}
					bReplaceAttr=true; // Перезаписываем данные
#endif					
				}

				CaplAttr *attr=0;

				if(!bReplaceAttr)
				{
					attr=new CaplAttr(id, 0, name,attr_type);
					attrs.Add(attr);
					map_attrs.Add(id,attr);
				}
				else
				{
					attr=prev_attr;
				}
				attr->entity_id=l_ent;
				attr->optional=opt;
				attr->redeclaring_id=l_redeclar;
				attr->vid = vid; 

				// чтение дополнительных типов
				j=0;
				while(aplGetWord(LPCTSTR(buf),i,s_type))
				{
					if((0==j)&&(attr_type==aplINSTANCE))
					{
						id=__atol((LPCTSTR)s_type);
						attr->inst_type_id=id;
						break;
					}
					if(j>0)
					{
						if(attr->add_types[j-1]==aplINSTANCE)
						{
							id=__atol((LPCTSTR)s_type);
							attr->inst_type_id=id;
							break;
						}
					}
					attr->add_types[j]=String2aplValueType(LPCTSTR(s_type));
					j++;
				}
			}
		}
		dict_error_line=0;

		// замена идентификаторов адресами
		for(i=0;i<attrs.Size;i++)
		{
			CaplAttr *attr=attrs[i];
			if(attr->inst_type_id!=0)
			{
				attr->inst_type = (CaplEntity*) map_ent_ids.GetByInP(attr->inst_type_id);
				if(attr->inst_type == 0)APL_THROW_EX(APLAPIERR_BADDICTIONARY,_T("Error postprocessing id 2 pointer"));
			}
			attr->entity = (CaplEntity*) map_ent_ids.GetByInP(attr->entity_id);
			if(attr->entity == 0) APL_THROW_EX(APLAPIERR_BADDICTIONARY,_T("Error postprocessing id 2 pointer"));

			if(attr->redeclaring_id != 0)
			{
				CaplAttr *attr_tmp = (CaplAttr*) map_attrs.GetByInP((int)(attr->redeclaring_id));
				if(attr_tmp != 0)
				{
					attr->redeclaring = attr_tmp;
					attr_tmp->bCanDerived = true;
				}
				else attr->redeclaring=0;
			}
		}
		if(bApend)  // Очищаем данные, т.к. это проще, чем проверять дублирование
		{
			for(i=0;i<entities.Size;i++)
			{
				CaplEntity *ent=entities[i];
				ent->supertypes.Clear();
				ent->all_subtypes.Clear();	
				ent->supertypes.Clear();
				ent->all_subtypes.Clear();
				ent->attrs.Clear();
				ent->all_attrs.Clear();
			}

			for(i=0;i<attrs.Size;i++)
			{
				attrs[i]->index=-2;
			}

		}

		// Преобразование номеров предков в указатели
		for(i=0;i<entities.Size;i++)
		{
			CaplEntity *ent=entities[i];
			for(j=0;j<ent->supertypes_id.Size;j++)
			{
				id=(apl_id)(ent->supertypes_id[j]);
				CaplEntity *e_type = (CaplEntity*) map_ent_ids.GetByInP(id);
				if(e_type == 0)APL_THROW_EX(APLAPIERR_BADDICTIONARY,_T("Error postprocessing id 2 pointer in supertypes"));
				ent->supertypes.SetAt(j,e_type);
			}
		}

		// построение сприска subtypе
		for(i=0;i<entities.Size;i++)
		{
			CaplEntity *ent=entities[i];
			for(j=0;j<ent->supertypes.Size;j++)
			{
				ent->supertypes[j]->subtypes.Add(ent);
			}
		}

		// построение списка атрибутов
		for(i=0;i<attrs.Size;i++)
		{
			CaplAttr *attr=attrs[i];
			attr->entity->attrs.Add(attr);
		}

		// построение списка наследуемых атрибутов
		for(i=0;i<entities.Size;i++)
		{
			CaplEntity *ent=entities[i];
			GetAllSupertypes(ent,ent->all_supertypes);
			GetAllSubtypes(ent,ent->all_subtypes);
			GetAllEntityAttrs(ent,ent->all_attrs);
		}

		// Проверка перегружаемых атрибутов
		for(i=0;i<entities.Size;i++)
		{
			CaplEntity *ent=entities[i];
			for(j=0;j<ent->all_attrs.Size;j++)
			{
				name=ent->all_attrs[j]->name;
				for(k=j+1;k<ent->all_attrs.Size;k++)
					if(ent->all_attrs[k]->name==name)
					{
						if(ent->all_attrs[j]->id != ent->all_attrs[k]->id)
						{
							// ЯАИ:  Для STEP-а это нормально
							if(m_CurSchema.Find(_T("config_control_design"))<0 &&  m_CurSchema.Find(_T("automotive_design"))<0)
							{
								CString str; str.Format(_T("The entity '%s'(%i) has several attributes '%s' with different identifiers "),
														(LPCTSTR)ent->name, ent->id, (LPCTSTR)name);
								APL_THROW_EX(APLAPIERR_BADDICTIONARY, str);
							}
						}
						ent->all_attrs.Remove(j);
						j--;
					}
			}
		}

		// добавил Дещере 27_09_01
		// Сортировка Entity и атрибутов
		qsort(entities.Data,entities.Size,sizeof(CaplEntity*),compare_aplEntity_id);
		qsort(attrs.Data,attrs.Size,sizeof(CaplAttr*),compare_aplAttr_id);


		// Определение индексов атрибутов
		for(i=0;i<entities.Size;i++)
		{
			CaplEntity *ent=entities[i];
			for(j=0;j<ent->all_attrs.Size;j++)
			{
				CaplAttr *attr=ent->all_attrs[j];
				if(attr->index==-2)  attr->index=j;
				if(attr->index!=j) attr->index=-1;
			}
		}

		// Установка индексов атрибутов и entity 
		m_MaxEntId=0; m_MaxEntId2=0; m_MinEntId2=999000;
		for(i=0;i<entities.Size;i++)
		{
			apl_id eid=entities[i]->id;
			if(eid<cMinId4ExtSchema) {if(eid>m_MaxEntId) m_MaxEntId=eid; }
			else
			{
				if(eid>m_MaxEntId2) m_MaxEntId2=eid;
				if(eid<m_MinEntId2) m_MinEntId2=eid;
			}
		}
		m_MaxEntId++;
		if(m_MinEntId2>m_MaxEntId2)m_MinEntId2=m_MaxEntId2;
		m_MaxEntId2++;

		if(0!=m_EntityIndex) delete [] m_EntityIndex;
		m_EntityIndex= (CaplEntity **)new BYTE[m_MaxEntId*sizeof(CaplEntity*)];
		for(i=0;i<(int)m_MaxEntId;i++) m_EntityIndex[i]=0;

		if(0!=m_EntityIndex2) delete [] m_EntityIndex2;
		m_EntityIndex2= (CaplEntity **)new BYTE[(m_MaxEntId2-m_MinEntId2)*sizeof(CaplEntity*)];
		for(i=(int)m_MinEntId2;i<(int)m_MaxEntId2;i++) m_EntityIndex2[i-m_MinEntId2]=0;
		
		for(i=0;i<entities.Size;i++)
		{
			j=entities[i]->id;
			if(j<cMinId4ExtSchema) m_EntityIndex[j]=entities[i];
			else m_EntityIndex2[j-m_MinEntId2]=entities[i];
		}

		m_MaxAttrId=0;
		m_MaxAttrId=0; m_MinAttrId2=99000;
		for(i=0;i<attrs.Size;i++)
		{
			CaplAttr *attr=attrs[i];
			if(attr->id<cMinId4ExtSchema){if(attr->id>m_MaxAttrId) m_MaxAttrId=attr->id; }
			else
			{
				if(attr->id>m_MaxAttrId2) m_MaxAttrId2=attr->id;
				if(attr->id<m_MinAttrId2) m_MinAttrId2=attr->id;
			}
		}

		if(m_MinAttrId2>m_MaxAttrId2) m_MinAttrId2=m_MaxAttrId2;
		m_MaxAttrId++;
		m_MaxAttrId2++;

		if(0!=m_AttrIndex) delete [] m_AttrIndex;
		m_AttrIndex=(CaplAttr**)new BYTE[m_MaxAttrId*sizeof(CaplAttr)];
		
		if(0!=m_AttrIndex2) delete [] m_AttrIndex2;
		m_AttrIndex2=(CaplAttr**)new BYTE[(m_MaxAttrId2-m_MinAttrId2)*sizeof(CaplAttr)];

		for(i=0; i<(int)m_MaxAttrId; i++)m_AttrIndex[i]=0;
		for(i=(int)m_MinAttrId2; i<(int)m_MaxAttrId2; i++) m_AttrIndex2[i-m_MinAttrId2]=0;

		for(i=0;i<attrs.Size;i++)
		{
			CaplAttr *attr=attrs[i];
			if(attr->id<cMinId4ExtSchema) m_AttrIndex[attr->id]=attr;
			else m_AttrIndex2[attr->id-m_MinAttrId2]=attr;
		}
	}
	catch(SaplErrorDescription error)
	{
		entities.Clear();
		attrs.Clear();
		if(dict_error_line>0)
		{
			buf.Format(_T(" Schema loading error! Error in line %i"),dict_error_line);
			if(error.m_err_descr != _T("")){buf+=_T("\n"); buf+=_T(" Error description: ")+error.m_err_descr;}
			error.m_err_descr=buf;
			//aplMessageBox(error.m_err_descr,MB_OK|MB_ICONERROR); - если нужен aplMessageBox, его выведет PrintError внутри SetLastErrorEx
		}
		else
		{
			//aplMessageBox(_T("\nSchema loading error! "),MB_OK|MB_ICONERROR); - если нужен aplMessageBox, его выведет PrintError внутри SetLastErrorEx
		}
		m_CurSchema.Empty();
		if(SaplErrorDescription::GetRetranslateMemoryExeption())
		{
#ifndef _MFC_VER
			if(m_MultyThreads)
			{
				SThreadContext* pErr = GetErrContext();
				if(pErr!=0)
				{
					if(!error.m_err_descr.IsEmpty() && !pErr->m_ErrorAnnotation.IsEmpty())error.m_err_descr += _T("\n");
					error.m_err_descr += pErr->m_ErrorAnnotation;pErr->m_ErrorAnnotation=_T("");
				}
				throw(SaplErrorDescription(error,_T(__FILE__),_T(__DATE__),__LINE__,0,__APL_FUNC__));
			}
#endif
			if(!error.m_err_descr.IsEmpty() && !m_ErrorAnnotation.IsEmpty())error.m_err_descr += _T("\n");
			error.m_err_descr += m_ErrorAnnotation;m_ErrorAnnotation=_T("");
			throw(SaplErrorDescription(error,_T(__FILE__),_T(__DATE__),__LINE__,0,__APL_FUNC__));
		}
		SetLastErrorEx(error);
		return false;
	}

	//m_CurSchema=schema;
	return true;
}


//**********************************************************************
bool CaplStepData::LoadDictionaryFromFile(LPCTSTR file_name, bool find_ex, bool bApend)
{
	QT_THREADS_DENY_FROM_READ
	CaplStackLogger stack_logger(_T("CaplStepData::LoadDictionaryFromFile"));

	if(aplIsStringEmpty(file_name)) {SetLastErrorWithFileInfo(APLAPIERR_BADSCHEMANAME);return false;}

	SetLastError(0);
	CString buf, word, name, s_id,s_ent,s_type;
	int i;
	if(!bApend) ClearDict();

	CFile dict_file;
	CaplDataBuf dict_dbuf;
	DWORD len = 0;
	BYTE* cbuf = 0;
	bool retval = false;

#ifndef _MFC_VER
	if(m_MultyThreads)
	{
		SThreadContext* pErr = GetErrContext();
		if(pErr!=0)
		{
			if(bApend)pErr->m_ErrorAnnotation=_T("\n Additional dictionary file: ");
			else pErr->m_ErrorAnnotation=_T("\n Dictionary file: ");
			pErr->m_ErrorAnnotation+= file_name;
		}
	}
	else
#endif
	{
		if(bApend)m_ErrorAnnotation=_T("\n Additional dictionary file: ");
		else m_ErrorAnnotation=_T("\n Dictionary file: ");
		m_ErrorAnnotation+= file_name;
	}

	try
	{

		if(!dict_file.Open(file_name,CFile::modeRead|CFile::shareDenyWrite))
		{
			if(!find_ex)
			{
				APL_THROW(APLAPIERR_DICT_FILE_NOT_OPEN);
			}
			CString path1,schema;
			path1=file_name;
			i=path1.GetLength()-path1.ReverseFind(_T('\\'));
			if(i!=-1){schema=path1.Right(i-1);}else{schema=file_name;}

			if(!aplGetModuleFilePath(path1))APL_THROW(APLAPIERR_DICT_FILE_NOT_OPEN);
			path1+=schema;
			if(!dict_file.Open(path1,CFile::modeRead|CFile::shareDenyWrite))
			{
#ifdef __linux__
				path1.GetEnvironmentVariable(_T("HOME"));
				path1+=aplDirRazd;
				path1+=schema;
				if(!dict_file.Open(path1,CFile::modeRead|CFile::shareDenyWrite))
				{
					APL_THROW(APLAPIERR_DICT_FILE_NOT_OPEN);
				}
#else
				APL_THROW(APLAPIERR_DICT_FILE_NOT_OPEN);
#endif
			}
		}


		len = (DWORD)dict_file.GetLength();
		cbuf = new BYTE[len+1];
		dict_file.Read(cbuf, len);
		cbuf[len] = 0;
	
		dict_dbuf.SetExternalData(cbuf, len+1);
		dict_file.Close();

		retval = LoadDictionaryFromMemory(dict_dbuf, bApend);
	}
	catch(SaplErrorDescription error)
	{
		if(!bApend)
        {
            entities.Clear();
            attrs.Clear();
            m_CurSchema.Empty();
        }
        retval = false;

		if(SaplErrorDescription::GetRetranslateMemoryExeption())
		{
			if(cbuf!=0) delete[] cbuf;
#ifndef _MFC_VER
			if(m_MultyThreads)
			{
				SThreadContext* pErr = GetErrContext();
				if(pErr!=0)
				{
					if(!error.m_err_descr.IsEmpty() && !pErr->m_ErrorAnnotation.IsEmpty())error.m_err_descr += _T("\n");
					error.m_err_descr += pErr->m_ErrorAnnotation;pErr->m_ErrorAnnotation=_T("");
				}
				throw(SaplErrorDescription(error,_T(__FILE__),_T(__DATE__),__LINE__,0,__APL_FUNC__));
			}
#endif
			if(!error.m_err_descr.IsEmpty() && !m_ErrorAnnotation.IsEmpty())error.m_err_descr += _T("\n");
			error.m_err_descr += m_ErrorAnnotation;m_ErrorAnnotation.Empty();
			throw(SaplErrorDescription(error,_T(__FILE__),_T(__DATE__),__LINE__, 0,__APL_FUNC__));
		}
		SetLastErrorEx(error);

	}
	if(cbuf!=0) delete[] cbuf;
	//m_CurSchema=schema;
	return retval;
}
//************************************************************************
void CaplStepData::ClearDict()
{
	QT_THREADS_DENY_FROM_READ
	SetLastError(0);
	ClearData();
	m_CurSchema=_T(""); //m_CurSchema2=_T("");
	m_CurSchemaVersion=0; //m_CurSchemaVersion2=0;
	m_CurAddSchemes.Clear();
	
	attrs.Clear();
	entities.Clear();

	if(0!=m_AttrIndex) delete[] m_AttrIndex;
	m_AttrIndex=0;

	if(0!=m_AttrIndex2) delete[] m_AttrIndex2;
	m_AttrIndex2=0;

	if(0!=m_EntityIndex) delete[] m_EntityIndex;
	m_EntityIndex=0;
	if(0!=m_EntityIndex2) delete[] m_EntityIndex2;
	m_EntityIndex2=0;

	m_MaxEntId=0; m_MaxEntId2=0; m_MinEntId2=0;
	m_MaxAttrId=0;

	m_EntytyNameMap.Clear();
}

//************************************************************************
void CaplStepData::SetMaxDoubleVal(double _MaxDoubleVal)
{
	MaxDoubleVal=_MaxDoubleVal;
}


//************************************************************************
void CaplStepData::ClearData()
{
	QT_THREADS_DENY_FROM_READ
	SetLastError(0);
	for(int i=0;i<entities.Size;i++) entities[i]->instances.Clear();
	instances.Clear();
	instances.SetSize(10000);
	//killed_instances.Clear();
	m_CurDataFile.Empty();
}
//************************************************************************
void CaplStepData::FreeInstancesAttr(aplExtent &instances)
{
	SetLastError(0);
	int i;
	for(i=0;i<instances.Size;i++)
	{
		CaplInstance *inst=instances[i];
		if(inst!=0) inst->ClearAttrs();
	}
}
//************************************************************************
CaplInstance  *CaplStepData::CreateInstance(CaplEntity *entity,bool is_temporary,bool create_attrs)
{
	QT_THREADS_DENY_FROM_READ
	if (0==entity) {SetLastErrorWithFileInfo(APLAPIERR_BADENT); return 0;}
	if(m_is_readonly && !is_temporary){SetLastErrorWithFileInfo(APLAPIERR_MODE_READ_ONLY); return 0;}
	SetLastError(0);
	CaplInstance *inst=new CaplInstance(this,entity,0,create_attrs);
	inst->SetTemporary(is_temporary);
	
	bool u=entity->instances.Unique;
	entity->instances.Unique=false;
	entity->instances.Add(inst);
	entity->instances.Unique=u;

	instances.Unique=false;
	instances.Add(inst);
	instances.Unique=true;
	return inst;
}

CaplInstance  *CaplStepData::CreateInstanceBN(LPCTSTR type_name,bool is_temporary,bool create_attrs)
{
	if (aplIsStringEmpty(type_name)) {SetLastErrorWithFileInfo(APLAPIERR_BADENT); return 0;}
	SetLastError(0);
	CaplEntity *ent = GetEntityBN(type_name);
	if (0 == ent) {SetLastErrorWithFileInfoExtText(APLAPIERR_BADENT, type_name); return 0;}
	return CreateInstance(ent, is_temporary, create_attrs);
}


//************************************************************************
bool CaplStepData::DeleteInstanceForced(CaplInstance *inst)
{
	QT_THREADS_DENY_FROM_READ
	bool retval;
	int pos;
	retval=DeleteInstance(inst,true);
	if(!retval)return false;
	if(inst->GetId()!=0 && instances.Sorted_id){
		// есть все условия для быстрого поиска делением пополам. Грех перебирать все
		pos=aplQFindInstIdInExtent(*((aplExtent*)(&instances)),inst->GetId(),instances.GetSize(),false);
	}else{
		// есть другие условия для быстрого поиска делением пополам. Грех перебирать все
		pos=aplQFindInstInExtent(*((aplExtent*)(&instances)),inst,true);
	}
	instances.Remove(pos);
	return true;
}
//************************************************************************
bool CaplStepData::DeleteInstance(CaplInstance *inst, bool bChekReference, bool bNoSetChanged)
{
	QT_THREADS_DENY_FROM_READ
	if (0==inst) {SetLastErrorWithFileInfo(APLAPIERR_BADINSTANCE); return false;}
	if (0==inst->GetType()) {SetLastErrorWithInstFileInfo(APLAPIERR_BADINSTANCE,inst); return false;}
	SetLastError(0);
	// чтобы сервер нас не отключил
	PumpTimerPaintStepData(m_dwLastPaintTickCount,true,true,true);

	if(m_TestAccessMode)
		if(inst->GetAccessmode()>aplOWN){SetLastErrorWithInstFileInfo(APLAPIERR_NOACCESSRIGHT,inst); return false;};
	int index=instances.Find(inst);
	if(index<0){SetLastErrorWithInstFileInfo(APLAPIERR_BADDATA,inst); return false;}

	// { Изменение идеологии удаленных Instance
		// старое
	//if(inst->GetId()!=0) killed_instances.Add(inst->GetId());
	int i=inst->GetType()->instances.Find(inst);
	inst->GetType()->instances.Remove(i);
	//instances.Remove(index);
		//новое
	inst->ClearAttrs();
	if(inst->attrs!=0){delete[] inst->attrs;}
	inst->attrs=0;
	inst->SetServiceParam(0);
	if(!bNoSetChanged)inst->SetDeleted();
	// Изменение идеологии удаленных Instance }

	// Поиск ссылающихся
	if(bChekReference)
	{
		int j,k,k1;
		CaplValue *value;
		for(i=0;i<instances.Size;i++)
		{
			CaplInstance *inst0=instances[i];
			if(0==inst0){instances.Remove(i); i--; continue;};

			CaplEntity *ent=inst0->GetType();
			if(0==ent) continue;
			if(0==inst0->attrs)continue;
			
			for(j=0; j<ent->all_attrs.Size;j++)
			{
				if(inst0->attrs[j].value.type==aplINSTANCE)
				{
					if(inst0->attrs[j].value.instval==inst)
					{
						if(!bNoSetChanged) inst0->attrs[j].changed=true;
						inst0->attrs[j].value.Clear();
					}
				}
				else if(inst0->attrs[j].value.type==aplAGGR)
				{
					CaplAggr *aggr=inst0->attrs[j].value.aggrval;
					if(aggr!=0)
					{
						for(k=0;k<aggr->GetSize();k++)
						{
							value=aggr->GetByIndex(k);
							if(value!=0)
							{
								if(value->type==aplINSTANCE)
								{
									if(value->instval==inst)
									{
										aggr->Remove(k);
										k--;
										if(!bNoSetChanged)
										{
											inst0->attrs[j].changed=true;
										}
									}
								}else if(value->type==aplAGGR)
								{
									CaplAggr *aggr1=value->aggrval;
									if(aggr1!=0)
									{
										for(k1=0;k1<aggr1->GetSize();k1++)
										{
											value=aggr1->GetByIndex(k1);
											if(value!=0)
											{
												if(value->type==aplINSTANCE)
												{
													if(value->instval==inst)
													{	
														aggr1->Remove(k1);
														if(!bNoSetChanged)
														{
															inst0->attrs[j].changed=true;
														}
														k1--;
													}
												}
											}
										}
									}
								}
							}
						} 
					}
				}
			}
		}
	}
	return true;
}
//************************************************************************
CaplInstance::CaplValueDefinition *CaplStepData::GetAttrValueByIndex(CaplInstance *inst,int real_indx)
{
	if(0==inst) return 0;
	if(0==inst->GetType()) return 0;
	if(real_indx<0) return 0;
		if(0==inst->attrs) 
			inst->attrs=new CaplInstance::CaplValueDefinition[inst->GetType()->all_attrs.Size];

	return &(inst->attrs[real_indx]);
}

//************************************************************************
CaplInstance::CaplValueDefinition *CaplStepData::GetAttrValue(CaplInstance *inst,CaplAttr *attr, bool bAutoCreate) const
{
	if(0==inst) return 0;
	if(0==inst->GetType()) return 0;
	if(!IsEntityAttr(inst->GetType(),attr)) return 0;

	if(bAutoCreate)
	{
		if(0==inst->attrs) 
			inst->attrs=new CaplInstance::CaplValueDefinition[inst->GetType()->all_attrs.Size];
	}
	else
	{
		if(0==inst->attrs) 
			return 0;
	}

	if(attr->index>=0)
		return &(inst->attrs[attr->index]);

	int i;

	CaplEntity *ent=inst->GetType();

	// Начинаем с конца, т.к. в начале обычно индексируемые атрибуты 
	for (i=ent->all_attrs.Size-1;i>=0;i--)
	{
		if(ent->all_attrs[i]==attr) 
		{
			return &(inst->attrs[i]);
		}
	}

	// На всякий случай Проверка перегружаемости
	if(attr->redeclaring!=0)
	{
		CaplAttr *attr_r=attr->redeclaring;
		for (i=ent->all_attrs.Size-1;i>=0;i--)
		{
			if(ent->all_attrs[i]==attr_r) 
			{
				return &(inst->attrs[i]);
			}
		}
	}
	SetLastWarningWithFileInfo(APLAPIERR_ATTRNSET);
	return 0;
}

CaplInstance::CaplValueDefinition *CaplStepData::GetAttrValueBN(CaplInstance *inst, LPCTSTR attr_name, bool bAutoCreate)
{
	if(0==inst) {SetLastErrorWithFileInfo(APLAPIERR_BADINSTANCE); return 0;}
	if(0==inst->GetType()) {SetLastErrorWithInstFileInfo(APLAPIERR_BADINSTANCE,inst); return 0;}
	return GetAttrValue(inst,GetAttrDefinition(inst->GetType(),attr_name), bAutoCreate);
}
//************************************************************************
//
// Возвращает указатель на entity по имени
//
CaplEntity *CaplStepData::GetEntityBN(LPCTSTR type_name)
{
	if (aplIsStringEmpty(type_name)) {SetLastErrorWithFileInfo(APLAPIERR_BADENT); return 0;}
	SetLastError(0);
	QT_THREAD_ENTER_CS
	CaplEntity *ent=(CaplEntity *)m_EntytyNameMap.GetP(type_name);
	QT_THREAD_LEAVE_CS
	if(0!=ent) return ent;

	int i=0;
	while(i < entities.Size)
	{
		CaplEntity *ent=entities[i];
		if(ent->name == type_name)
		{
			QT_THREAD_ENTER_CS
			m_EntytyNameMap.Add(type_name,ent);
			QT_THREAD_LEAVE_CS
			return ent;
		}
		i++;
	}
	return 0;
}
// Добавил Дещере 27_09_01
//Возвращает указатель на Entity
CaplEntity* CaplStepData::GetEntityById(apl_id id)
{
	if(entities.Size<1) return 0;
	if(id<=0)return 0;

	if(0==m_EntityIndex) return 0;

	if(id<cMinId4ExtSchema)
	{
		if(id>m_MaxEntId) return 0;
		return m_EntityIndex[id];
	}
	if(id<m_MinEntId2 || id>m_MaxEntId2)  return 0;
	return m_EntityIndex2[id-m_MinEntId2];

	return NULL;
}

//Возвращает указатель на Attr
CaplAttr* CaplStepData::GetAttrDefinitionById(apl_id id)
{
	if(attrs.GetSize()<1) return 0;
	if(id==0)return 0;

	if(m_AttrIndex==0) return 0;
	if(id<cMinId4ExtSchema)
	{
		if(id>=m_MaxAttrId) return 0;
		return m_AttrIndex[id];
	}
	if(id<m_MinAttrId2 || id>=m_MaxAttrId2) return 0;
	return m_AttrIndex2[id-m_MinAttrId2];
}

//
// Возвращает указатель на complex entity по списку имен составляющих
//
CaplEntity *CaplStepData::GetComplexEntityBN(CStringArray &names)
{
	if(names.GetSize()==0){SetLastErrorWithFileInfo(APLAPIERR_BADENT); return 0;};
	CaplTAggr <CaplEntity*,CaplEntity*,APLAGGR_LIST> ent_s;
	CaplEntity *ent;
	int i;
	// заполнение массива указателей на entity, входящих в состав complex entity
	for(i = 0; i < names.GetSize(); i++)
	{
		ent = GetEntityBN(LPCTSTR(names[i]));
		if (0==ent) {SetLastErrorWithFileInfo(APLAPIERR_BADENT); return 0;};
		ent_s.Add(ent);
	}
	// проход по всем существующим entity
	for (i = 0; i < entities.Size; i++)
	{
		if(entities[i]->supertypes.Size != ent_s.Size) continue;
		bool found = true;
		// проход по entity из списка составляющих ent_s
		for(int j = 0; j < ent_s.Size; j++)
		{
			ent = ent_s[j]; 
			bool flag = false;
			// поиск отдельной entity
			for(int k = 0; k < entities[i]->supertypes.Size; k++)
			{
				if(entities[i]->supertypes[k] == ent)
					{flag = true; break;}
			}
			if(!flag) {found = false; break;}
		}
		if(found) return entities[i];
	}
	return 0;
}

///////////////////////////////////////////////////////////
CaplEntity *CaplStepData::GetComplexEntityBNP21(CStringArray &names)
{
	if(names.GetSize()==0){SetLastErrorWithFileInfo(APLAPIERR_BADENT); return 0;};
	CaplTAggr <CaplEntity*,CaplEntity*,APLAGGR_LIST> ent_s;
	CaplEntity *ent;
	int i;
	// заполнение массива указателей на entity, входящих в состав complex entity
	for(i = 0; i < names.GetSize(); i++)
	{
		ent = GetEntityBN(LPCTSTR(names[i]));
		if (0==ent) {SetLastErrorWithFileInfo(APLAPIERR_BADENT); return 0;};
		ent_s.Add(ent);
	}
	// проход по всем существующим entity
	for (i = 0; i < entities.Size; i++)
	{
		if(entities[i]->all_supertypes.Size != ent_s.Size) continue;
		bool found = true;
		// проход по entity из списка составляющих ent_s
		for(int j = 0; j < ent_s.Size; j++)
		{
			ent = ent_s[j]; 
			bool flag = false;
			// поиск отдельной entity
			for(int k = 0; k < entities[i]->all_supertypes.Size; k++)
			{
				if(entities[i]->all_supertypes[k] == ent)
					{flag = true; break;}
			}
			if(!flag) {found = false; break;}
		}
		if(found) return entities[i];
	}
	return 0;
}

//************************************************************************
CaplAttr *CaplStepData::GetAttrDefinitionBN(LPCTSTR entity_name, LPCTSTR attr_name)
{
	if(aplIsStringEmpty(entity_name)) return 0;
	if(aplIsStringEmpty(attr_name)) return 0;
	return GetAttrDefinition(GetEntityBN(entity_name),attr_name);
}

CaplAttr *CaplStepData::GetAttrDefinition(CaplEntity *entity, LPCTSTR attr_name) const
{
	if(0==entity) return 0;
	if(aplIsStringEmpty(attr_name)) return 0;
	int i;
	for(i=0;i<entity->all_attrs.Size;i++)
	{
		if(entity->all_attrs[i]->name==attr_name)
			return entity->all_attrs[i];
	}
	for(i=0;i<entity->attrs.Size;i++)
	{
		if(entity->attrs[i]->name==attr_name)
		{
			if(entity->attrs[i]->redeclaring!=0) return entity->attrs[i]->redeclaring;
			else return entity->attrs[i];
		}
	}
	return 0;

}
//************************************************************************
void CaplStepData::GetAllSubtypes(CaplEntity *entity, CaplTAggr <CaplEntity*,CaplEntity*,APLAGGR_UNIQUE|APLAGGR_LIST> &all_st)
{
	if(0==entity) return;
	int i;
	for(i=0; i<entity->subtypes.Size;i++)
	{
		all_st.Add(entity->subtypes[i]);
		GetAllSubtypes(entity->subtypes[i],all_st);
	}
}

void CaplStepData::GetAllSupertypes(CaplEntity *entity, CaplTAggr <CaplEntity*,CaplEntity*,APLAGGR_UNIQUE|APLAGGR_LIST> &all_st)
{
	if(0==entity) return;
	int i;
	for(i=0; i<entity->supertypes.Size;i++)
	{
		all_st.Add(entity->supertypes[i]);
		GetAllSupertypes(entity->supertypes[i],all_st);
	}
}
//************************************************************************
void CaplStepData::GetAllEntityAttrs(CaplEntity *entity,CaplTAggr <CaplAttr*,CaplAttr*,APLAGGR_UNIQUE|APLAGGR_LIST> &all_a)
{
	if(0==entity) return;
	int i;
	for(i=0; i<entity->supertypes.Size;i++)
		GetAllEntityAttrs(entity->supertypes[i],all_a);
	for(i=0;i<entity->attrs.Size;i++)
	{
		if(entity->attrs[i]->redeclaring==0)
		{
			if(entity->attrs[i]->vid==_T('e')) all_a.Add(entity->attrs[i]);
		}
	}
}
//************************************************************************
bool CaplStepData::GetAttrBN(CaplInstance *inst, LPCTSTR attr_name, CaplValue &value) const 
{
	value.Clear();
	CaplValue *val;
	if(!GetAttrBN(inst,attr_name,&val)) return false;
	if(val!=0) value.Set(*val);
	return true;
}

//#define SetLastErrorWithFileInfo(ErrCode) SetLastErrorEx(ErrCode,false,_T(__FILE__),__LINE__);

bool CaplStepData::GetAttrBN(CaplInstance *inst, LPCTSTR attr_name, CaplValue **value) const 
{
	*value=0;
//	if(0==inst) {SetLastErrorWithFileInfo(APLAPIERR_BADINSTANCE); return false;}
	if(0==inst) {SetLastErrorWithInstFileInfo(APLAPIERR_BADINSTANCE,inst); return false;}
	if(inst==(CaplInstance*)-1) {SetLastErrorWithInstFileInfo(APLAPIERR_BADINSTANCE,inst); return false;}
	if(0==inst->GetType()) {SetLastErrorWithInstFileInfo(APLAPIERR_BADINSTANCE,inst); return false;}
	if(aplIsStringEmpty(attr_name)) {SetLastErrorWithInstAttrFileInfo(APLAPIERR_BADATTR,inst,0); return false;}
	CaplAttr* attr=GetAttrDefinition(inst->GetType(),attr_name);
	if(m_TestAccessMode)
		if(inst->GetAccessmode()>aplRO) {SetLastErrorWithInstAttrFileInfo(APLAPIERR_NOACCESSRIGHT,inst,attr); return false;}
	if(!GetAttr(inst,attr,value))return false;
	return true;
}

bool CaplStepData::GetAttr(CaplInstance *inst,CaplAttr *attr,CaplValue &value) const
{
	value.Clear();
	CaplValue *val;
	if(!GetAttr(inst,attr,&val)) return false;
	if(val!=0) value.Set(*val);
	return true;
}

bool CaplStepData::GetAttr(CaplInstance *inst,CaplAttr *attr,CaplValue **value) const
{
	*value=0;
	if(0==inst) {SetLastErrorWithInstAttrFileInfo(APLAPIERR_BADINSTANCE,inst,attr); return false;}
	if(inst==(CaplInstance*)-1) {SetLastErrorWithInstAttrFileInfo(APLAPIERR_BADINSTANCE,inst,attr); return false;}
	if(0==inst->GetType()) {SetLastErrorWithInstAttrFileInfo(APLAPIERR_BADINSTANCE,inst,attr); return false;}
	if(0==attr) {SetLastErrorWithInstAttrFileInfo(APLAPIERR_BADATTR,inst,attr); return false;}
	if(m_TestAccessMode)
		if(inst->GetAccessmode() > aplRO) {SetLastErrorWithInstAttrFileInfo(APLAPIERR_NOACCESSRIGHT,inst,attr); return false;}
	SetLastError(0);

	CaplInstance::CaplValueDefinition *av=GetAttrValue(inst,attr);
	if(av==0) 
	{
		SetLastWarningWithFileInfo(APLAPIERR_ATTRNSET);		
		return false;
	}
	*value=&(av->value);
	return true;
}

//**********************************************************************
#define APL_GETATTR_SIMPLE_VALUE \
	CaplValue *val; \
	if(!GetAttr(inst,attr,&val)) return false; \
	if(val!=0) val->Get(value); \
	return true; \

#define APL_GETATTRBN_SIMPLE_VALUE \
	CaplValue *val; \
	if(!GetAttrBN(inst,attr_name,&val)) return false; \
	if(val!=0) val->Get(value); \
	return true; \

bool CaplStepData::GetAttr(CaplInstance *inst,CaplAttr *attr, _std_string &value) const
	{ value.clear(); APL_GETATTR_SIMPLE_VALUE }
bool CaplStepData::GetAttrBN(CaplInstance *inst, LPCTSTR attr_name, _std_string &value) const
	{ value.clear(); APL_GETATTRBN_SIMPLE_VALUE }
bool CaplStepData::GetAttr(CaplInstance *inst,CaplAttr *attr, int &value) const
	{ value=0; APL_GETATTR_SIMPLE_VALUE }
bool CaplStepData::GetAttrBN(CaplInstance *inst, LPCTSTR attr_name, int &value) const
	{ value=0; APL_GETATTRBN_SIMPLE_VALUE }
bool CaplStepData::GetAttr(CaplInstance *inst,CaplAttr *attr, double &value) const
	{ value=0; APL_GETATTR_SIMPLE_VALUE }
bool CaplStepData::GetAttrBN(CaplInstance *inst, LPCTSTR attr_name, double &value) const
	{ value=0; APL_GETATTRBN_SIMPLE_VALUE }
bool CaplStepData::GetAttr(CaplInstance *inst,CaplAttr *attr, CString &value) const
	{ value.Empty();APL_GETATTR_SIMPLE_VALUE }
bool CaplStepData::GetAttrBN(CaplInstance *inst, LPCTSTR attr_name, CString &value) const
	{ value.Empty();APL_GETATTRBN_SIMPLE_VALUE }
bool CaplStepData::GetAttr(CaplInstance *inst,CaplAttr *attr, pCaplInstance &value) const
	{ value=0; APL_GETATTR_SIMPLE_VALUE }
bool CaplStepData::GetAttrBN(CaplInstance *inst, LPCTSTR attr_name, pCaplInstance &value) const
	{ value=0; APL_GETATTRBN_SIMPLE_VALUE }
bool CaplStepData::GetAttr(CaplInstance *inst,CaplAttr *attr, bool &value) const
	{ value=false; APL_GETATTR_SIMPLE_VALUE }
bool CaplStepData::GetAttrBN(CaplInstance *inst, LPCTSTR attr_name, bool &value) const
	{ value=false; APL_GETATTRBN_SIMPLE_VALUE }
bool CaplStepData::GetAttr(CaplInstance *inst,CaplAttr *attr, CaplAggr &value) const
	{ value.Clear(); APL_GETATTR_SIMPLE_VALUE }
bool CaplStepData::GetAttrBN(CaplInstance *inst, LPCTSTR attr_name, CaplAggr &value) const
	{ value.Clear(); APL_GETATTRBN_SIMPLE_VALUE }
bool CaplStepData::GetAttr(CaplInstance *inst,CaplAttr *attr, aplExtent &value) const
	{ value.Clear(); APL_GETATTR_SIMPLE_VALUE }
bool CaplStepData::GetAttrBN(CaplInstance *inst, LPCTSTR attr_name, aplExtent &value) const
	{ value.Clear(); APL_GETATTRBN_SIMPLE_VALUE }

//**********************************************************************
//**********************************************************************
//**********************************************************************
bool CaplStepData::ChekAttrType(CaplInstance *inst, CaplAttr *attr, aplValueType type, CaplInstance *inst_val)
{
	if(0==attr)  return false;
	if(0==inst) return false;
	if(0==inst->GetType()) return false;
	if(type==aplNOTYPE) return true;

	bool loop=true;
	while(loop)
	{
		if(attr->type==aplSELECT)
		{
			if(type==aplINSTANCE) return true;
			// В обменных файлах в select может быть любой тип, а в Pss только instance
			if(m_CurSchema==_T("apl_pss_a")) return false;
			return true;
		}
		if(attr->type==aplINSTANCE)
		{
			if(type!=aplINSTANCE) return false;
			if(inst_val==0) return true;
			if(inst_val==(CaplInstance *)-1) return false;
#ifdef _DEBUG
			// в дебаге проверяем, что параметр inst_val является валидной инстансой.
			// Если не валидна - сразу упадет.
			// В релизе невалидная инстанс приведет к падению в другом месте
			CaplEntity *ent=inst_val->GetType();
			if(0==ent) aplMessageBox(_T("Invalid inst_val->GetType()!!!"),MB_OK|MB_ICONSTOP);
#endif
			if(attr->inst_type!=0)
			{
				return IsKindOf(inst_val,attr->inst_type);
			}
			return true;
		}
		if(attr->type==type) return true;
		if((attr->type==aplREAL)&&(type==aplINTEGER)) return true;
		if((attr->type==aplBOOL)&&(type==aplLOGICAL)) return true;
		if((attr->type==aplLOGICAL)&&(type==aplBOOL)) return true;
		if((attr->type==aplENUMERATION)&&(type==aplSTRING)) return true;

		// Поиск перегружающего
		loop=false;
		for(int i=0; i<inst->GetType()->attrs.Size;i++)
		{
			if(inst->GetType()->attrs[i]->redeclaring==attr)
			{
				attr=inst->GetType()->attrs[i]; 
				loop=true;
				break;
			}
		}
	}
	return false;
}

bool CaplStepData::ChekAttrType(CaplInstance *inst, CaplAttr *attr,CaplValue &value)
{
	return ChekAttrType(inst, attr,value.type,value.instval);
}
//**********************************************************************

bool CaplStepData::PutAttrBN(CaplInstance *inst, LPCTSTR attr_name, CaplValue &value)
{
	if(0==inst) {SetLastErrorWithFileInfo(APLAPIERR_BADINSTANCE); return false;}
	if(inst==(CaplInstance*)-1) {SetLastErrorWithFileInfo(APLAPIERR_BADINSTANCE); return false;}
	if(0==inst->GetType()) {SetLastErrorWithInstFileInfo(APLAPIERR_BADINSTANCE,inst); return false;}
	if(aplIsStringEmpty(attr_name)) {SetLastErrorWithInstFileInfo(APLAPIERR_BADATTR,inst); return false;}
	return  PutAttr(inst,GetAttrDefinition(inst->GetType(),attr_name),value);
}

bool CaplStepData::PutAttr(CaplInstance *inst,CaplAttr *attr, CaplValue &value)
{
	QT_THREADS_DENY_FROM_READ
	if(0==inst) {SetLastErrorWithFileInfo(APLAPIERR_BADINSTANCE); return false;}
	if(inst==(CaplInstance*)-1) {SetLastErrorWithFileInfo(APLAPIERR_BADINSTANCE); return false;}
	if(0==inst->GetType()) {SetLastErrorWithInstFileInfo(APLAPIERR_BADINSTANCE,inst); return false;}

	if(0==attr) {SetLastErrorWithInstAttrFileInfo(APLAPIERR_BADATTR,inst,attr); return false;}
	if(m_TestAccessMode)
		if(inst->GetAccessmode()>aplRW) {SetLastErrorWithInstAttrFileInfo(APLAPIERR_NOACCESSRIGHT,inst,attr); return false;}
	if(!ChekAttrType(inst, attr,value.type,value.instval))
		{SetLastErrorWithInstAttrFileInfo(APLAPIERR_BADTYPE,inst,attr); return false;}
	SetLastError(0);
	
	CaplInstance::CaplValueDefinition *val=GetAttrValue(inst,attr,true);
	if(val==0){SetLastErrorWithInstAttrFileInfo(APLAPIERR_BADDATA,inst,attr); return false;}
	if(m_check_val_in_putattr){if(val->value.IsEqu(value)){return true;}}
	val->value.Set(value);
	val->changed=true;
	inst->m_dt_changed_attr=(COleDateTime::GetCurrentTime()).m_dt;
	return true;
}
#define APL_PUTATTR_SIMPLE_VALUE(TYPE,VALUE) \
	if(0==inst) {SetLastErrorWithInstAttrFileInfo(APLAPIERR_BADINSTANCE,inst,attr); return false;} \
	if(inst==(CaplInstance*)-1) {SetLastErrorWithInstAttrFileInfo(APLAPIERR_BADINSTANCE,inst,attr); return false;}\
	if(0==inst->GetType()) {SetLastErrorWithInstAttrFileInfo(APLAPIERR_BADINSTANCE,inst,attr); return false;} \
	if(0==attr) {SetLastErrorWithInstAttrFileInfo(APLAPIERR_BADATTR,inst,attr); return false;} \
	if(m_TestAccessMode==true) \
		{if(inst->GetAccessmode()>aplRW) {SetLastErrorWithInstAttrFileInfo(APLAPIERR_NOACCESSRIGHT,inst,attr); return false;};} \
	if(!ChekAttrType(inst, attr,TYPE,VALUE)){SetLastErrorWithInstAttrFileInfo(APLAPIERR_BADTYPE,inst,attr); return false;} \
	CaplInstance::CaplValueDefinition *val=GetAttrValue(inst,attr,true);\
	if(val==0) {SetLastErrorWithInstAttrFileInfo(APLAPIERR_BADATTR,inst,attr); return false;}\
	if(m_check_val_in_putattr){CaplValue value_test; value_test.Set(value);if(val->value.IsEqu(value_test)){return true;}}\
	val->value.Set(value);\
	val->changed=true;\
	return true;\

#define APL_PUTATTRBN_SIMPLE_VALUE \
	if(0==inst) {SetLastErrorWithInstFileInfo(APLAPIERR_BADINSTANCE,inst); return false;} \
	if(inst==(CaplInstance*)-1) {SetLastErrorWithInstFileInfo(APLAPIERR_BADINSTANCE,inst); return false;}\
	if(0==inst->GetType()) {SetLastErrorWithInstFileInfo(APLAPIERR_BADINSTANCE,inst); return false;} \
	return PutAttr(inst,GetAttrDefinition(inst->GetType(),attr_name),value); \


bool CaplStepData::PutAttr(CaplInstance *inst, CaplAttr *attr,  int value)
	{APL_PUTATTR_SIMPLE_VALUE(aplINTEGER,0)}
bool CaplStepData::PutAttrBN(CaplInstance *inst, LPCTSTR attr_name,  int value)
	{APL_PUTATTRBN_SIMPLE_VALUE}
bool CaplStepData::PutAttr(CaplInstance *inst, CaplAttr *attr,  double value)
	{APL_PUTATTR_SIMPLE_VALUE(aplREAL,0)}
bool CaplStepData::PutAttrBN(CaplInstance *inst, LPCTSTR attr_name,  double value)
	{APL_PUTATTRBN_SIMPLE_VALUE}
bool CaplStepData::PutAttr(CaplInstance *inst, CaplAttr *attr, LPCTSTR value)
	{APL_PUTATTR_SIMPLE_VALUE(aplSTRING,0)}
bool CaplStepData::PutAttrBN(CaplInstance *inst, LPCTSTR attr_name, LPCTSTR value)
	{APL_PUTATTRBN_SIMPLE_VALUE}
bool CaplStepData::PutAttr(CaplInstance *inst, CaplAttr *attr, bool value)
	{APL_PUTATTR_SIMPLE_VALUE(aplBOOL,0)}
bool CaplStepData::PutAttrBN(CaplInstance *inst, LPCTSTR attr_name,  bool value)
	{APL_PUTATTRBN_SIMPLE_VALUE}
bool CaplStepData::PutAttr(CaplInstance *inst, CaplAttr *attr, CaplInstance *value)
	{APL_PUTATTR_SIMPLE_VALUE(aplINSTANCE,value)}
bool CaplStepData::PutAttrBN(CaplInstance *inst, LPCTSTR attr_name,  CaplInstance *value)
	{APL_PUTATTRBN_SIMPLE_VALUE}
bool CaplStepData::PutAttr(CaplInstance *inst, CaplAttr *attr, const CaplAggr &value)
	{APL_PUTATTR_SIMPLE_VALUE(aplAGGR,0)}
bool CaplStepData::PutAttrBN(CaplInstance *inst, LPCTSTR attr_name,  const CaplAggr &value)
	{APL_PUTATTRBN_SIMPLE_VALUE}
bool CaplStepData::PutAttr(CaplInstance *inst, CaplAttr *attr, const aplExtent &value)
	{APL_PUTATTR_SIMPLE_VALUE(aplAGGR,0)}
bool CaplStepData::PutAttrBN(CaplInstance *inst, LPCTSTR attr_name,  const aplExtent &value)
	{APL_PUTATTRBN_SIMPLE_VALUE}

//**********************************************************************
//**********************************************************************
//**********************************************************************

bool CaplStepData::GetEntityExtentBN(LPCTSTR ent_name,aplExtent &extent)
{
	extent.Clear();
	if(aplIsStringEmpty(ent_name)) {SetLastErrorWithFileInfo(APLAPIERR_BADENT); return false;}
	return GetEntityExtent(GetEntityBN(ent_name),extent);
}
bool CaplStepData::GetEntityExtent(CaplEntity *entity,aplExtent &extent)
{
	extent.Clear();
	if(0==entity) {SetLastErrorWithFileInfo(APLAPIERR_BADENT); return false;}
	SetLastError(0);
	bool u=extent.Unique;
	extent.Unique=false;
	extent.Append(entity->instances);
	for(int i=0;i<entity->all_subtypes.Size;i++)
		extent.Append(entity->all_subtypes[i]->instances);
	extent.Unique=u;

	return true;
}
//************************************************************************
bool CaplStepData::IsKindOfBN(CaplInstance *inst, LPCTSTR type_name)
{
	if(aplIsStringEmpty(type_name)) {SetLastErrorWithInstEnttFileInfo(APLAPIERR_BADENT,inst,(CaplEntity*)0); return false;}
	return IsKindOf(inst,GetEntityBN(type_name));
}

bool CaplStepData::IsKindOf(CaplInstance *inst,CaplEntity *entity)
{
	if(0==inst) {SetLastErrorWithInstEnttFileInfo(APLAPIERR_BADINSTANCE,inst,entity); return false;}
	if(inst==(CaplInstance*)-1) {SetLastErrorWithInstEnttFileInfo(APLAPIERR_BADINSTANCE,inst,entity); return false;}
	if(0==inst->GetType()) {SetLastErrorWithInstEnttFileInfo(APLAPIERR_BADINSTANCE,inst,entity); return false;}
	if(0==entity) {SetLastErrorWithInstEnttFileInfo(APLAPIERR_BADENT,inst,entity); return false;}
	SetLastError(0);
	CaplEntity *ent=inst->GetType();
	if(ent==entity) return true;
	for(int i=0; i<ent->all_supertypes.Size;i++)
	{
		if(ent->all_supertypes[i]==entity)return true;
	}
	return false;
}

bool CaplStepData::IsKindOfEntity(CaplEntity *entity_tested,CaplEntity *entity_parent)
{
	if(entity_tested==0) {SetLastErrorWithEnttEnttFileInfo(APLAPIERR_BADENT,entity_tested,entity_parent); return false;}
	if(entity_parent==0) {SetLastErrorWithEnttEnttFileInfo(APLAPIERR_BADENT,entity_tested,entity_parent); return false;}
	SetLastError(0);
	if(entity_tested==entity_parent) return true;
	for(int i=0; i<entity_tested->all_supertypes.Size;i++)
	{
		if(entity_tested->all_supertypes[i]==entity_parent)return true;
	}
	return false;
}

//************************************************************************
//**********************************************************************
//************************************************************************
bool CaplStepData::SaveToTextFile(LPCTSTR file_name,bool save_temporary)
{
	if(aplIsStringEmpty(file_name)) {SetLastErrorWithFileInfo(APLAPIERR_BADFILENAME); return false;}
	SetLastError(0);
	int i,j,instmapsize;
	CString buf,buf1;


	CaplStringFile file;
	if(!file.Open(file_name,CFile::modeCreate|CFile::modeWrite)){
		SetLastErrorWithFileInfoExtText(APLAPIERR_FILE_IO,file_name); return false;}

#ifdef _UNICODE
	file.SetFileEncoding(aplUTF8);
#endif
	
	pCaplInstance *instmap=0;
	// Внимание! instances всегда должен быть отсортирован по id!
	//
	//CSortClass::SortInstByInst(instances);//qSortExtent(*(aplExtent*)&instances); 
	instmapsize=instances.Size;
	instmap=new pCaplInstance[instmapsize];

	for(i=0;i<instmapsize;i++) instmap[i]=instances[i];
	CSortClass::SortArrayByInst(&instmap,instmapsize);

	file.WriteString(_T("apl_text_data V 0.1\n"));
	file.WriteString(m_CurSchema);
	QT_THREAD_ENTER_CS
	buf.Format(_T("\n%i  - MaxId"), m_MaxInstNum);
	QT_THREAD_LEAVE_CS
	file.WriteString(buf);
	buf.Format(_T("\n%i  - Instances"), instances.Size);
	file.WriteString(buf);
	for(i=0;i<instances.Size;i++)
	{
		CaplInstance *inst_i=instmap[i]; //-V595
		if(inst_i==NULL) continue;
		if(inst_i->GetType()==0) continue;
		if(inst_i->GetTemporary() && !save_temporary) continue;//16.01.2003
		
		buf.Format(_T("\n\n#%i=%s <%i,0x%p>"),i+1,LPCTSTR(inst_i->GetType()->name), inst_i->GetId(),inst_i);
		file.WriteString(buf);

		if(inst_i->attrs!=0)
		{
			CaplEntity *ent=inst_i->GetType();
			if(ent!=0)
			{
				for(j=0;j<ent->all_attrs.Size;j++)
				{
					inst_i->attrs[j].value.Print(buf1,instmap,instmapsize);
					buf.Format(_T("\n   %s = %s"),LPCTSTR(ent->all_attrs[j]->name),LPCTSTR(buf1));
					file.WriteString(buf);
				}
			}
		}
	}
	if(instmap!=0) delete[] instmap;
	file.WriteString(_T("\n\nend"));
	file.Flush();
	file.Close();
	return true;
}
//**********************************************************************
bool CaplStepData::LoadFromTextFile(LPCTSTR file_name)
{
	QT_THREADS_DENY_FROM_READ
	if(aplIsStringEmpty(file_name)) {SetLastErrorWithFileInfo(APLAPIERR_BADFILENAME); return false;}
	
	ClearData();
	bool ret=true;

	CaplStringFile file;
	if(!file.Open(file_name,CFile::modeRead)) 
		{SetLastErrorWithFileInfoExtText(APLAPIERR_FILE_IO,file_name); return false;}

	CaplInstance *lastinst=0;
	pCaplInstance *instmap=0;
	try
	{
		CString buf,buf1;
		int i,pos,k,instmapsize=0;;
		file.ReadString(buf);
		if(buf!=_T("apl_text_data V 0.1"))APL_THROW(APLAPIERR_INVALIDDATAFORMAT);
		file.ReadString(buf);
		if(!LoadDictionary(buf)) APL_THROW(-1);
		file.ReadString(buf);
		m_MaxInstNum=_atoi(LPCTSTR(buf));
		file.ReadString(buf);
		instmapsize=_atoi(LPCTSTR(buf));
		instmap=new pCaplInstance[instmapsize+1];

		CString name,sval;
		UINT fpos=file.GetPosition();

		//чтение instance		
		while(file.ReadString(buf))
		{
			buf.TrimLeft();
			if(buf==_T("end")) break;
			if(buf.GetLength()<2)continue;
			if(buf[0]==_T('#'))
			{
				lastinst=0;
				i=buf.Find(_T("="));
				pos=i+1;
				if(i!=-1)
				{
					k=_atoi(LPCTSTR(buf)+1);
					aplGetWord(LPCTSTR(buf),pos,name);
					if(name!=_T(""))
					{
						if(k<=instmapsize)
						{
							CaplEntity *ent=GetEntityBN(name);
							if(0==ent) 
							{
								instmap[k]=0;
								continue;
							}
							lastinst=CreateInstance(ent);
							instmap[k]=lastinst;

							aplGetWord(LPCTSTR(buf),pos,buf1);
							if(buf1==_T("<"))
							{
								aplGetWord(LPCTSTR(buf),pos,buf1);
								lastinst->SetId(_atoi(LPCTSTR(buf1)));
							}
						}
						else
							i=0;
					}
				}
			}
		}

		//чтение атрибутов		
		file.SetPosition(fpos);
		while(file.ReadString(buf))
		{
			buf.TrimLeft();
			if(buf==_T("end")) break;
			if(buf.GetLength()<2)continue;
			if(buf[0]==_T('#'))
			{
				lastinst=0;
				i=buf.Find(_T("="));
				if(i!=-1)
				{
					k=_atoi(LPCTSTR(buf)+1);
					lastinst=instmap[k];
				}
			}
			else
			{
				// чтение атрибутов
				i=buf.Find(_T("="));
				if(i!=-1)
				{
					if(0==lastinst) continue;
					pos=0;
					aplGetWord(LPCTSTR(buf),pos,(name));
					aplGetWord(LPCTSTR(buf),pos,(buf1));
					if(buf1==_T("="))
					{
						CaplValue value;
						value.ReadFromString(LPCTSTR(buf),pos,instmap,instmapsize);
						if(value.type!=aplNOTYPE)
						{
							CaplAttr *attr=GetAttrDefinition(lastinst->GetType(),name);
							if(0==attr) continue;
							PutAttr(lastinst,attr,value);
						}
					}
				}
			}
		}
		m_Default_TextFormat=true;
	}
	catch(SaplErrorDescription error)
	{
		ClearData();
		if(error.m_err_code!=-1)SetLastErrorEx(error);// ClearData сбрасывает ошибку, поэтому ее надо вызывать до SetLastErrorEx
		ret = false;
	}
	if(instmap!=0) delete instmap;
	file.Close();
	return ret;
	
}
//**********************************************************************
bool CaplStepData::LoadP21(LPCTSTR file_name, aplP21Header &header)
{
	QT_THREADS_DENY_FROM_READ
	if(aplIsStringEmpty(file_name)) 
	{
		SetLastErrorWithFileInfo(APLAPIERR_BADFILENAME); 
		return false;
	}
	
	ClearData();
	bool ret = true;
	bool bShowMsgIgnoreAttr=true;
#if _MSC_VER >= 1400
	ULONGLONG size_f_l;
#endif
	int flen;
	CFile file;
	
	if(!file.Open(file_name,CFile::modeRead)) 
	{
		SetLastErrorWithFileInfoExtText(APLAPIERR_FILE_IO,file_name); 
		return false;
	}

#if _MSC_VER >= 1400
	size_f_l=file.GetLength();
	if(size_f_l>MAX_FILE_LENGTH_32_BIT)
	{
		return false;
	}
	flen=(DWORD)size_f_l;
#else
	flen=file.GetLength();
#endif
	// для чтения из файла нужен однобайтный буфер
	char *c_buf=new char[flen+1];
	file.Read(c_buf,flen);
	c_buf[flen]='\0';
	file.Close();

	CString buf;
#ifdef _UNICODE
	CaplStringAdapter str_ad(c_buf);
	buf = (LPCTSTR)str_ad;
#else // #ifdef _UNICODE
	buf = c_buf;
#endif // #ifdef _UNICODE
	if(c_buf!=0) delete[] c_buf;

#ifdef _MFC_VER
	CaplSetResourceHandle res_handle(module_inst);

	CEfLoadDlg dlg;
	dlg.Create(IDD_EF_LOAD);
	dlg.m_inst.SetPos(0);
	dlg.m_attr.SetPos(0);
	dlg.ShowWindow(TRUE);
#endif

	CaplInstance *lastinst = 0;
	pCaplInstance *instmap = 0;
	try
	{
		int pos = 0;		// позиция в строке
//		CString buf = "";	// буфер для чтения из файла
		CString token;		// строка для чтения в нее лексемы из буфера buf
				
	//
	//                   Чтения заголовка файла
	//
		// iso-10303-21
		aplGetWord(buf, pos, token);
		if (token != _T("iso-10303-21")) APL_THROW(APLAPIERR_INVALIDDATAFORMAT);
		aplGetWord(buf, pos, token);
		if (token != _T(';')) APL_THROW(APLAPIERR_INVALIDDATAFORMAT);
		// header
		aplGetWord(buf, pos, token);
		if (token != _T("header")) APL_THROW(APLAPIERR_INVALIDDATAFORMAT);
		aplGetWord(buf, pos, token);
		if (token != _T(';')) APL_THROW(APLAPIERR_INVALIDDATAFORMAT);
		// file_description
		aplGetWord(buf, pos, token);
		if (token != _T("file_description")) APL_THROW(APLAPIERR_INVALIDDATAFORMAT);
		aplGetWord(buf, pos, token);if (token != '(') APL_THROW(APLAPIERR_INVALIDDATAFORMAT);
		SkipBrackets(buf, pos);
		aplGetWord(buf, pos, token);
		if (token != _T(';')) APL_THROW(APLAPIERR_INVALIDDATAFORMAT);
		// file_name и считывание aplP21Header
		aplGetWord(buf, pos, token);
		if (token != _T("file_name")) APL_THROW(APLAPIERR_INVALIDDATAFORMAT);
		aplGetWord(buf, pos, token);
		if (token != _T('(')) APL_THROW(APLAPIERR_INVALIDDATAFORMAT);
			// name
		aplGetWord(buf, pos, token);token.Remove(_T('\''));ConvertP21String(token, aplWIN7BIT, aplWIN);
		header.name = token;		
		aplGetWord(buf, pos, token);
		if (token != _T(',')) APL_THROW(APLAPIERR_INVALIDDATAFORMAT);
			// time_stamp
		aplGetWord(buf, pos, token);token.Remove(_T('\''));
		header.time_stamp = token;	
		aplGetWord(buf, pos, token);
		if (token != _T(',')) APL_THROW(APLAPIERR_INVALIDDATAFORMAT);
			// author
		aplGetWord(buf, pos, token);
		if (token != _T('(')) APL_THROW(APLAPIERR_INVALIDDATAFORMAT);
		while (1)
		{
			aplGetWord(buf, pos, token);
			if (token == _T(')')) break;
			else if (token != _T(','))
			{token.Remove(_T('\''));ConvertP21String(token, aplWIN7BIT, aplWIN);header.author.Add(token);}
		}
		aplGetWord(buf, pos, token);
		if (token != _T(',')) APL_THROW(APLAPIERR_INVALIDDATAFORMAT);
			// organization
		aplGetWord(buf, pos, token);
		if (token != _T('(')) APL_THROW(APLAPIERR_INVALIDDATAFORMAT);
		while (1)
		{
			aplGetWord(buf, pos, token);
			if (token == _T(')')) break;
			else if (token != _T(','))
				{token.Remove(_T('\''));ConvertP21String(token, aplWIN7BIT, aplWIN);header.organization.Add(token);}
		}
		aplGetWord(buf, pos, token);
		if (token != _T(',')) APL_THROW(APLAPIERR_INVALIDDATAFORMAT);
			// preprocessor_version
		aplGetWord(buf, pos, token);token.Remove(_T('\''));ConvertP21String(token, aplWIN7BIT, aplWIN);
		header.preprocessor_version = token;
		aplGetWord(buf, pos, token);
		if (token != _T(',')) APL_THROW(APLAPIERR_INVALIDDATAFORMAT);
			// originating_system
		aplGetWord(buf, pos, token);token.Remove(_T('\''));ConvertP21String(token, aplWIN7BIT, aplWIN);
		header.originating_system = token;
		aplGetWord(buf, pos, token);
		if (token != _T(',')) APL_THROW(APLAPIERR_INVALIDDATAFORMAT);
			// authorisation
		aplGetWord(buf, pos, token);token.Remove(_T('\''));ConvertP21String(token, aplWIN7BIT, aplWIN);
		header.authorisation = token;
		aplGetWord(buf, pos, token);
		if (token != _T(')')) APL_THROW(APLAPIERR_INVALIDDATAFORMAT);
		aplGetWord(buf, pos, token);
		if (token != _T(';')) APL_THROW(APLAPIERR_INVALIDDATAFORMAT);
		// file_schema
		aplGetWord(buf, pos, token);
		if (token != _T("file_schema")) APL_THROW(APLAPIERR_INVALIDDATAFORMAT);
		aplGetWord(buf, pos, token);
		if (token != _T('(')) APL_THROW(APLAPIERR_INVALIDDATAFORMAT);
		aplGetWord(buf, pos, token);
		if (token != _T('(')) APL_THROW(APLAPIERR_INVALIDDATAFORMAT);
		aplGetWord(buf, pos, token);token.Remove(_T('\''));
		if(!LoadDictionary(token)) APL_THROW(-1);

		// сместимся на секцию данных
		while (1)
		{
			if (!aplGetWord(buf, pos, token))
				APL_THROW(APLAPIERR_INVALIDDATAFORMAT);
			if (token == _T("data"))
				break;
		}
		aplGetWord(buf, pos, token);
		if (token != _T(';')) APL_THROW(APLAPIERR_INVALIDDATAFORMAT);
		int DataPos=pos; 
		
		int index = 0;	 // индекс в массиве instmap
		int max_index=0; // максимальный id instance в обменном файле
		
		///////////////////////////////////////////////////
		//  ч т е н и е   i n s t a n c e 		
		///////////////////////////////////////////////////
#ifdef _MFC_VER		
		dlg.m_inst.SetRange32(0,flen);
#endif
		while(1)
		{
			if(global_process_wm_paint)
			{
				PumpTimerPaintStepData(m_dwLastPaintTickCount,true,false,false);
			}
			CString token;
			if (!aplGetWord(buf, pos, token))
				APL_THROW(APLAPIERR_INVALIDDATAFORMAT);
			
#ifdef _MFC_VER		
			dlg.m_inst.SetPos(pos);
#endif	
			
			if (token == _T("endsec"))
				break;
			if (token[0] == _T('#'))
			{
				long InstanceId = __atol(LPCTSTR(token) + 1);
				if(InstanceId>max_index) max_index=InstanceId;
				aplGetWord(buf, pos, token);
				if (token != _T("="))
					APL_THROW(APLAPIERR_INVALIDDATAFORMAT);
				aplGetWord(buf, pos, token);
				if (token == _T('('))
				{
					///////////////////////////////////////
					// c o m p l e x   i n s t a n c e 
					///////////////////////////////////////
					CStringArray names;
					while (1)
					{
						aplGetWord(buf, pos, token);
						if (token == _T(')')) break;
						names.Add(token);
						aplGetWord(buf, pos, token);
						if (token != _T('(')) APL_THROW(APLAPIERR_INVALIDDATAFORMAT);
						SkipBrackets(buf, pos);	
					}
					CaplEntity *tmp_entity = GetComplexEntityBNP21(names);
					lastinst = CreateInstance(tmp_entity); 
				}
				else
				{
					///////////////////////////////////////
					// s i m p l e   i n s t a n c e
					///////////////////////////////////////
					lastinst = CreateInstanceBN(token);
					aplGetWord(buf, pos, token);
					if (token != _T('('))
						APL_THROW(APLAPIERR_INVALIDDATAFORMAT);
					SkipBrackets(buf, pos);
				}
				//
				if (lastinst)	// если получилось создать instance
					lastinst->SetId(InstanceId);
				//
				aplGetWord(buf, pos, token);
				if (token != _T(";"))
					APL_THROW(APLAPIERR_INVALIDDATAFORMAT);
				index++;
			}
			else
				APL_THROW(APLAPIERR_INVALIDDATAFORMAT);
		}
		// ayatsk Создание instmap
		int instmapsize = max_index+1;
		instmap = new pCaplInstance[instmapsize+1];
		for (long counter = 0; counter < instmapsize; counter++) instmap[counter] = 0;
		
		for(int i=0;i<instances.Size;i++)
		{
			instmap[instances[i]->GetId()]=instances[i];
		}
		// ayatsk instmap создана
		
		// позиционирование на секцию данных
		pos = DataPos;

		///////////////////////////////////////////////////
		//  ч т е н и е   а т р и б у т о в		
		///////////////////////////////////////////////////
#ifdef _MFC_VER		
		dlg.m_attr.SetRange32(0,instances.Size);
#endif
		int attr_num=0;
		index = 0;	// индекс в массиве instmap
		while(1)
		{
			if(global_process_wm_paint)
			{
				PumpTimerPaintStepData(m_dwLastPaintTickCount,true,false,false);
			}
	
			CString token;
			if (!aplGetWord(buf, pos, token))
				APL_THROW(APLAPIERR_INVALIDDATAFORMAT);
			if (token == _T("endsec"))
				break;
		
			if (token[0] == _T('#'))
			{
				attr_num++;
#ifdef _MFC_VER		
				dlg.m_attr.SetPos(attr_num);
#endif
				long InstanceId = __atol(LPCTSTR(token) + 1);
				aplGetWord(buf, pos, token);
				if (token != _T("="))
					APL_THROW(APLAPIERR_INVALIDDATAFORMAT);
				aplGetWord(buf, pos, token);
				if (token == _T('('))
				{
					///////////////////////////////////////
					// c o m p l e x   i n s t a n c e 
					///////////////////////////////////////
					// цикл по составляющим 
					while (1)
					{
						aplGetWord(buf, pos, token);
						if (token == _T(')')) break;
						//
						CaplEntity *tmp_entity = GetEntityBN(token);
						int N_Attrs = tmp_entity->attrs.Size;
						//
						aplGetWord(buf, pos, token);
						if (token != _T('('))
							APL_THROW(APLAPIERR_INVALIDDATAFORMAT);
						
						// цикл по атрибутам составляющей
						CaplValue value;
						bool b_no_first_attr=false;
						for (int i = 0; i < N_Attrs; i++)
						{
							if(tmp_entity->attrs[i]->vid!=_T('e')) continue;
							if (b_no_first_attr)
							{
								aplGetWord(buf, pos, token);
								if (token != _T(','))
									APL_THROW(APLAPIERR_INVALIDDATAFORMAT);
							}
							b_no_first_attr=true;
							value.ReadFromP21String(buf, pos,instmap, instmapsize);
							if(value.type != aplNOTYPE)
							{
								if(instmap[InstanceId]!=0)
								{
									if(aplINTEGER==value.type  && aplREAL==tmp_entity->attrs[i]->type)
									{
										// Корректируем некоректное чтение типов
										double d=value.ival;
										value.Set(d);
									}
									PutAttr(instmap[InstanceId], tmp_entity->attrs[i], value);
								}
							}
						}
						aplGetWord(buf, pos, token);
						if (token != _T(')'))
						{
							if (token == _T(',')) // Атрибутов больше чем в схеме
							{
								if(bShowMsgIgnoreAttr)
								{
									aplMessageBox(_T("В файле больше атрибутов чем в схеме.\n\n Доп. Атрибуты будут проигнорированы!"));
									bShowMsgIgnoreAttr=false;
								}
								while (token == _T(','))
								{
									value.ReadFromP21String(buf, pos,instmap, instmapsize);
									aplGetWord(buf, pos, token);
								}
							}
							if (token != _T(')'))
							{
								APL_THROW(APLAPIERR_INVALIDDATAFORMAT);
							}
						}
					}
				}
				else
				{
					///////////////////////////////////////
					// s i m p l e   i n s t a n c e
					///////////////////////////////////////
					aplGetWord(buf, pos, token);
					if (token != _T('('))
						APL_THROW(APLAPIERR_INVALIDDATAFORMAT);
					int N_Attrs = 0;
					if(instmap[InstanceId]!=0)
						N_Attrs = instmap[InstanceId]->GetType()->all_attrs.Size;
					if (!N_Attrs)	// если атрибутов нет
						SkipBrackets(buf, pos);
					else	
					{
						CaplValue value;
						for (int i = 0; i < N_Attrs; i++)
						{
							if (i > 0)
							{
								aplGetWord(buf, pos, token);
								if (token != _T(','))
								{
									if(token == _T(')'))  // ЯАИ 05.07.2013: Если текущий словарь более новый - в нем больше атрибутов, чем в файле
									{
										pos--;
										break; // больше нет атрибутов
									}
									else 
										APL_THROW(APLAPIERR_INVALIDDATAFORMAT);
								}
							}
							//
							value.ReadFromP21String(buf, pos, instmap, instmapsize);
							//					
							if(value.type != aplNOTYPE)
							{
								CaplAttr *attr=instmap[InstanceId]->GetType()->all_attrs[i];
								if(aplINTEGER==value.type  && aplREAL==attr->type)
								{
									// Корректируем некоректное чтение типов
									double d=value.ival;
									value.Set(d);
								}
								PutAttr(instmap[InstanceId], attr , value);
							}
						}
						aplGetWord(buf, pos, token);
						if(token==_T(",")) SkipBrackets(buf, pos);
						else if (token != _T(')'))
							APL_THROW(APLAPIERR_INVALIDDATAFORMAT);
					}
				}
				//
				aplGetWord(buf, pos, token);
				if (token != _T(";"))
					APL_THROW(APLAPIERR_INVALIDDATAFORMAT);
				index++;
			}
			else
				APL_THROW(APLAPIERR_INVALIDDATAFORMAT);
		}
	}	
	catch(SaplErrorDescription error)
	{
		ClearData();
		if(error.m_err_code!=-1)SetLastErrorEx(error);// ClearData сбрасывает ошибку, поэтому ее надо вызывать до SetLastErrorEx
		ret = false;
	}
#ifdef _MFC_VER		
	dlg.ShowWindow(FALSE);
#endif	
	// освободим память под массивом instmap
	if(instmap != 0) delete instmap;
	return ret;
}
//**********************************************************************
bool CaplStepData::SaveP21(LPCTSTR file_name, aplP21Header &header, aplRusSaveMode mode, bool bSaveRealId)
{
	if(aplIsStringEmpty(file_name)) {SetLastErrorWithFileInfo(APLAPIERR_BADFILENAME); return false;}
	SetLastError(0);

	CString sBuf;
	if(!SaveP21ToDataBuf(sBuf,header,mode,bSaveRealId,file_name)) return false;

	CaplStringFile file;
	if(!file.Open(file_name,CaplFile::modeCreate|CaplFile::modeWrite))
	{SetLastErrorWithFileInfoExtText(APLAPIERR_FILE_IO,file_name); return false;}

	file.SetFileEncoding(aplANSI);
	file.WriteString(sBuf);

	file.Flush();
	file.Close();
	return true;
}

bool CaplStepData::SaveP21ToDataBuf(CString &sDataBuf, aplP21Header &header, aplRusSaveMode mode, bool bSaveRealId, LPCTSTR filename)
{
	sDataBuf.Empty();
	sDataBuf.GetBuffer(64*1024);
	sDataBuf.ReleaseBuffer();


	///////////////////////////////////////////////////////
	//   з а г о л о в о ч н а я   с е к ц и я
	///////////////////////////////////////////////////////
	CString buf,buf1;
	CString tmpstr = _T("");
	sDataBuf+=_T("ISO-10303-21;\nHEADER;\n/* Exchange file generated using APL SDAI v0.1 */\n");
	sDataBuf+=_T("FILE_DESCRIPTION(\n/* description */ ('This is an APL SDAI to STEP file in Part 21 Format'),\n");
	sDataBuf+=_T("/* implementation_level */ '1');\n");
	// file name
	tmpstr = filename;
	int i=tmpstr.ReverseFind(_T('\\'));
	if(i>=0) tmpstr=tmpstr.Right(tmpstr.GetLength()-i-1);
	i=tmpstr.ReverseFind(_T(':'));
	if(i>=0) tmpstr=tmpstr.Right(tmpstr.GetLength()-i-1);
	ConvertP21String(tmpstr, aplWIN, mode);
	buf.Format(_T("FILE_NAME(\n/* name */ '%s',\n"), LPCTSTR(tmpstr));
	sDataBuf+=buf;
	// time_stamp
	if(header.time_stamp!=_T(""))buf1=header.time_stamp;
	else
	{
		COleDateTime time=COleDateTime::GetCurrentTime();
		buf1=time.Format(_T("%Y-%m-%dT%H:%M:%S"));
	}
	buf.Format(_T("/* time_stamp */ '%s',\n"), LPCTSTR(buf1));
	sDataBuf+=buf;
	// запись списка author
	sDataBuf+=_T("/* author */ (");
	for (i = 0; i < header.author.GetSize(); i++)
	{
		if (i > 0) sDataBuf+=_T(",\n");
		tmpstr = header.author[i];
		ConvertP21String(tmpstr, aplWIN, mode);
		buf.Format(_T("'%s'"), LPCTSTR(tmpstr));
		sDataBuf+=buf;
	}
	if (header.author.GetSize() == 0)
		sDataBuf+=_T("''");
	sDataBuf+=_T("),\n");
	// organization
	sDataBuf+=_T("/* organization */ (");
	for (i = 0; i < header.organization.GetSize(); i++)
	{
		if (i > 0) sDataBuf+=_T(",\n");
		tmpstr = header.organization[i];
		ConvertP21String(tmpstr, aplWIN, mode);
		buf.Format(_T("'%s'"), LPCTSTR(tmpstr));
		sDataBuf+=buf;
	}
	if (header.organization.GetSize() == 0)
		sDataBuf+=_T("''");
	sDataBuf+=_T("),\n");
	// preprocessor_version
	tmpstr = header.preprocessor_version;
	ConvertP21String(tmpstr, aplWIN, mode);
	buf.Format(_T("/* preprocessor_version */ '%s',\n"), LPCTSTR(tmpstr));
	sDataBuf+=buf;
	// originating_system
	tmpstr = header.originating_system;
	ConvertP21String(tmpstr, aplWIN, mode);
	buf.Format(_T("/* originating_system */ '%s',\n"), LPCTSTR(tmpstr));
	sDataBuf+=buf;
	// authorization
	tmpstr = header.authorisation;
	ConvertP21String(tmpstr, aplWIN, mode);
	buf.Format(_T("/* authorisation */ '%s');\n"), LPCTSTR(tmpstr));
	sDataBuf+=buf;
	// file_schema
	CString schema_upper = m_CurSchema;
	schema_upper.MakeUpper();
	buf.Format(_T("FILE_SCHEMA (('%s'));\nENDSEC;\n\nDATA;"), LPCTSTR(schema_upper));
	sDataBuf+=buf;
	
	//static char s_header[]="ISO-10303-21;\nHEADER;\n/* Exchange file generated using APL SDAI v0.0 */\n"
	//			"FILE_DESCRIPTION(\n/* description */ ('This is an APL DAI to STEP file in Part 21 Format'),\n"
	//			"/* implementation_level */ '2;1');\nFILE_NAME(\n/* name */ '%s',\n"
	//			"/* time_stamp */ '0-0-0T0:0:0+00:00',\n/* author */ (''),\n/* organization */ (''),\n"
	//			"/* preprocessor_version */ '',\n/* originating_system */ '',\n"
	//			"/* authorisation */ '');\nFILE_SCHEMA (('%s'));"
	//			"\nENDSEC;\n\nDATA;";
	//buf.Format(s_header,file_name, schema_upper);
	//file.WriteString(buf);
	
	///////////////////////////////////////////////////////
	//           i n s t a n c e s
	///////////////////////////////////////////////////////

	// Внимание! instances всегда должен быть отсортирован по id!
	//
	//if(!bSaveRealId) CSortClass::SortInstByInst(instances);//if(!bSaveRealId) qSortExtent(*(aplExtent*)&instances);//	instances.Sort();



	int instmapsize = instances.Size;
	pCaplInstance *instmap = new pCaplInstance[instmapsize];
	for(i = 0; i < instances.Size; i++) instmap[i] = instances[i];
	
	if(!bSaveRealId) CSortClass::SortArrayByInst(&instmap,instmapsize);
	else  CSortClass::SortArrayById(&instmap,instmapsize);

	for(i = 0; i < instmapsize; i++)
	{
		if(instmap[i]==NULL) continue;
		if(instmap[i]->GetType()==0) continue; //05.10.200
		if(instmap[i]->GetTemporary()) continue;// 16.01.2003
		if(global_process_wm_paint)
		{
			PumpTimerPaintStepData(m_dwLastPaintTickCount,true,false,false);
		}
		//instid=aplQuickFind((int*)instmap,instmapsize,(int)(value->instval));
		//if(instid==-1)	instid=0;
		if (instmap[i]->GetType()->complex)
		{	
			///////////////////////////////////////////////
			// запись complex instance
			///////////////////////////////////////////////
			if(bSaveRealId) buf.Format(_T("\n#%i=("), instmap[i]->GetId());
			else 	buf.Format(_T("\n#%i=("), i + 1);
			sDataBuf+=buf;
			// запись составляющих 
			for (int j = 0; j < instmap[i]->GetType()->supertypes.Size; j++)
			{
				if (j == 0)
				{
					for(int j1=0;j1<instmap[i]->GetType()->supertypes[0]->all_supertypes.Size;j1++)
					{
						CString tmp = instmap[i]->GetType()->supertypes[0]->all_supertypes[j1]->name;
						tmp.MakeUpper();

						buf.Format(_T("\n%s("), LPCTSTR(tmp));
						
						sDataBuf+=buf;
						// запись атрибутов
						bool b_no_first_attr=false;
						for (int k = 0; k < instmap[i]->GetType()->supertypes[j]->all_supertypes[j1]->attrs.Size; k++)
						{
							if(instmap[i]->GetType()->supertypes[j]->all_supertypes[j1]->attrs[k]->vid!=_T('e'))continue;

							if(b_no_first_attr) sDataBuf+=_T(", ");	// разделение атрибутов запятыми
							b_no_first_attr=true;

							CString buf1;
							//if(is_attr_overloaded) buf1=_T("*");
							//else
							{
								CaplValue *val = 0;
								GetAttr(instmap[i], instmap[i]->GetType()->supertypes[j]->all_supertypes[j1]->attrs[k], &val);
								if(val == 0)
									buf1 = _T("$");
								else if(val->type == aplNOTYPE)
									buf1=_T("$");
								else 
									//val->Print(buf1, instmap, instmapsize, mode,instances[i]->type->supertypes[j]->all_supertypes[j1]->attrs[k],bSaveRealId);
									val->Print(buf1, instmap, instmapsize, mode,instmap[i]->GetType()->supertypes[j]->all_supertypes[j1]->attrs[k],bSaveRealId);
								
								if(buf1==_T("$"))
								{
									if(instmap[i]->GetType()->supertypes[j]->all_supertypes[j1]->attrs[k]->bCanDerived)
										buf1 = _T("*");
								}
							}
							sDataBuf+=buf1;
						}

						sDataBuf+=_T(")");
					}
				}
				// запись остальных составляющих
				CString tmp = instmap[i]->GetType()->supertypes[j]->name;
				tmp.MakeUpper();
				buf.Format(_T("\n%s("), LPCTSTR(tmp));
				sDataBuf+=buf;

				// запись атрибутов каждой составляющей
				bool b_no_first_attr=false;
				for (int k = 0; k < instmap[i]->GetType()->supertypes[j]->attrs.Size; k++)
				{
					if( instmap[i]->GetType()->supertypes[j]->attrs[k]->vid!=_T('e')) continue;
					if(b_no_first_attr)sDataBuf+=_T(", ");	// разделение атрибутов запятыми
					b_no_first_attr=true;
					CaplValue *val = 0;
					GetAttr(instmap[i], instmap[i]->GetType()->supertypes[j]->attrs[k], &val);
					CString buf1;
					if(val == 0) 
						buf1 = _T("$");
					else if(val->type == aplNOTYPE)
						buf1=_T("$");
					else 
						val->Print(buf1, instmap, instmapsize, mode,instmap[i]->GetType()->supertypes[j]->attrs[k],bSaveRealId);
					
					if(buf1==_T("$"))
					{
						if(instmap[i]->GetType()->supertypes[j]->attrs[k]->bCanDerived)
							buf1 = _T("*");
					}
					
					sDataBuf+=buf1;
				}
				sDataBuf+=_T(")");	// закрывающая скобка для составляющей 
			}
			sDataBuf+=_T("\n);");	// закрывающая скобка для complex instance
		}
		else
		{	
			///////////////////////////////////////////////
			// запись обычного instance
			///////////////////////////////////////////////
			CString buf1 = instmap[i]->GetType()->name;
			buf1.MakeUpper();
			if(bSaveRealId)buf.Format(_T("\n#%i=%s( "), instmap[i]->GetId(), LPCTSTR(buf1));
			else buf.Format(_T("\n#%i=%s( "), i + 1, LPCTSTR(buf1));
			sDataBuf+=buf;
			// запись атрибутов
			for(int j = 0; j < instmap[i]->GetType()->all_attrs.Size; j++)
			{
				if(j > 0) sDataBuf+=_T(", ");	// разделение атрибутов запятыми
				CaplValue *val = 0;
				GetAttr(instmap[i], instmap[i]->GetType()->all_attrs[j], &val);

				if(val == 0) 
					buf1 = _T("$");
				else if(val->type == aplNOTYPE)
					buf1=_T("$");
				else 
					val->Print(buf1, instmap, instmapsize, mode,instmap[i]->GetType()->all_attrs[j],bSaveRealId);

				if(buf1==_T("$"))
				{
					if(instmap[i]->GetType()->all_attrs[j]->bCanDerived) //-V595
						buf1 = _T("*");
				}

				sDataBuf+=buf1;
			}
			sDataBuf+=_T(");");	// закрывающая скобка для instance
		}
	}	// конец цикла по instances
	///////////////////////////////////////////////////////
	//    о к о н ч а н и е   ф а й л а
	///////////////////////////////////////////////////////

	if(instmap!=0) delete instmap;
	sDataBuf+=_T("\n\nENDSEC;\nEND-ISO-10303-21;");

	return true;
}
//**********************************************************************
//**********************************************************************
//**********************************************************************
void aplAppendBufValue(CaplDataBuf &data, TInstIntMap *inst_map, CaplValue *value)
{
	aplValueType type=aplNOTYPE;
	UINT32 i, size, instid;
	CaplValue *val;
	if(value==0){data.Add(&type,sizeof(type));return;}
	//if(instmap==0) {data.Add(&type,sizeof(type));return;}
	type=value->type;
	BYTE microtype=(BYTE)type;
	data.Add(&microtype,sizeof(microtype));
	switch (type)
	{
	case aplINTEGER: data.Add(&(value->ival),sizeof(value->ival)); break;
	case aplREAL: data.Add(&(value->rval),sizeof(value->rval)); break;
	case aplBOOL: data.Add(&(value->bval),sizeof(value->bval)); break;
	case aplLOGICAL: data.Add(&(value->lval),sizeof(value->lval)); break;
	case aplINSTANCE:
	case aplSELECT:
		{
		TInstIntMap_it it = inst_map->find(value->instval);
		if(it!=inst_map->end())
			instid=it->second;
		else
			instid = 0;
		// if(instid==-1) instid=0;
		data.Add(&instid,sizeof(instid)); 
		}
		break;
	case aplSTRING: 
	case aplENUMERATION: 
		if(value->sval==0) value->Set(_T(""));
		size=_strlen(value->sval)+1;
#ifdef _UNICODE
		if(data.m_ansi_string)
		{
			CaplStringAdapter str_ad(value->sval);
			#ifndef _MFC_VER
				str_ad.m_bIsAnsiUtf8=false; // В буфере либо 1251, либо UCS2
			#endif
			data.Add(&size, sizeof(size));
			data.Add((void*)(const char*)str_ad,size);
			break;
		}
		else
		{
			#ifndef __linux__
				ASSERT(2==sizeof(TCHAR));
				data.Add(&size, sizeof(size));
				data.Add(value->sval,size*sizeof(TCHAR)); 
			#else
				ASSERT(4==sizeof(TCHAR));
				BYTE *strUcs2=0; 
				int strUcs2Len=0;
				CString ErrMess(_T(""));
				if(bConvertTextWithIconv(aplUTF32LE,(BYTE*)value->sval,size*sizeof(TCHAR),aplUCS2,strUcs2,strUcs2Len, 0, &ErrMess))
				{
					if(0!=strUcs2)
					{
						data.Add(&size, sizeof(size));
						data.Add(strUcs2,strUcs2Len);
						delete[] strUcs2;
					}
					else
					{
						size = 0;
						data.Add(&size, sizeof(size));
					}
				}
				else
				{
					if(!ErrMess.IsEmpty())
						LOG_SHOW_ERROR_FC(APL_T("Ошибка при кодировании строки для записи в буфер: %s. Некорректная строка: %s"),
										  (LPCTSTR)ErrMess, (LPCTSTR)value->sval);
					size = 0;
					data.Add(&size, sizeof(size));
				}
			#endif

			break;
		}
#else // #ifdef _UNICODE
		if(data.m_ansi_string)
		{
			data.Add(&size,sizeof(size));
			data.Add(value->sval,size); 
		}
		else
		{
			//APL_THROW_EX(APLAPIERR_INCOMPATIBLDATAFORMAT,CString( APL_T("Для работы с буфером нужна UNICODE версия")));
			CaplStringAdapter str_ad(value->sval);
			CStringW str_w = (const wchar_t*)str_ad;
			data.Add(&size,sizeof(size));
			data.Add(str_w.GetBuffer(),size*2); 
		}
#endif // #ifdef _UNICODE
		 break;
	case aplINTEGRATEDBIN: 
		data.Add(&(value->intbinval->blobId),sizeof(value->intbinval->blobId)); 
		break;
	case aplBINARY: 
		size=value->binval->m_Size;
		data.Add(&size,sizeof(size));
		data.Add(value->binval->m_data,size); break;
	case aplAGGR: 
		size=value->aggrval->GetSize();
		data.Add(&size,sizeof(size));
		for(i=0;i<size;i++)
		{
			val=value->aggrval->GetByIndex(i);
			aplAppendBufValue(data,inst_map,val);
		}
		break;
	case aplBLOB:   break;
	case aplNOTYPE: break;
	}
}
//**********************************************************************
bool aplReadValueFromBuf(CaplDataBuf &dbuf, pCaplInstance *instmap,CaplValue *value, bool bUseExternalString)
{
	if(value==0) return false;
	value->Clear();
	aplValueType type=aplNOTYPE;
	UINT32 i, size=0, instindex=0;
	INT32 ival=0;
	double rval=0;
	bool bval=false;
	BYTE lval=-1;
	CaplInstance *instval;
	BYTE microtype;
	dbuf.Read(&microtype,sizeof(microtype));
	type=(aplValueType)microtype;
	switch (type)
	{
	case aplINTEGER: dbuf.Read(&ival,sizeof(ival));value->Set(ival); break;
	case aplREAL: dbuf.Read(&rval,sizeof(rval));value->Set(rval); break;
	case aplBOOL: dbuf.Read(&bval,sizeof(bval));value->Set(bval); break;
	case aplLOGICAL: dbuf.Read(&lval,sizeof(lval));value->Set(lval); break;
	case aplINSTANCE: 
	case aplSELECT: 
		if(instmap==0)return false;
		dbuf.Read(&instindex,sizeof(instindex));
		if((int)instindex!=-1)
		{
			instval=instmap[instindex];
			if(instval!=0) 	value->Set(instval); else value->Clear();
		}
		break;
	case aplSTRING: 
	case aplENUMERATION: 
		dbuf.Read(&size,sizeof(size));
#ifdef _UNICODE
		if(dbuf.m_ansi_string)
		{
			CaplStringAdapter str_ad((const char*)(dbuf.m_data + dbuf.GetCurPos()));		
			#ifndef _MFC_VER
				str_ad.m_bIsAnsiUtf8=false; // В буфере либо 1251, либо UCS2
			#endif
			value->Set((TCHAR*)str_ad);
			dbuf.SetCurPos(dbuf.GetCurPos()+size);
		}
		else
		{
			#ifndef __linux__
				ASSERT(2==sizeof(TCHAR));
				if(bUseExternalString)
				{
					//value->sval = (wchar_t*)(dbuf.m_data + dbuf.GetCurPos());
					value->SetExternalString((LPCTSTR)(dbuf.m_data + dbuf.GetCurPos()));
					dbuf.SetCurPos(dbuf.GetCurPos()+size*sizeof(TCHAR));
				}
				else
				{
					value->sval=new TCHAR[size];
					dbuf.Read(value->sval,size*sizeof(TCHAR));
				}
			#else
				ASSERT(4==sizeof(TCHAR));
				BYTE *strUcs2=0, *strUtf32=0; 
				int strUcs2Len=0, strUtf32Len=0;

				strUcs2=(BYTE*)dbuf.m_data + dbuf.GetCurPos();
				strUcs2Len=size*2; // 2байта на символ
				dbuf.SetCurPos(dbuf.GetCurPos()+ strUcs2Len);
				CString ErrMess(_T(""));
				if(bConvertTextWithIconv(aplUCS2, strUcs2, strUcs2Len ,aplUTF32LE, strUtf32, strUtf32Len, 0, &ErrMess))
				{
					if(0!=strUtf32) value->sval=(TCHAR*)strUtf32;
				}
				else if(!ErrMess.IsEmpty())
					LOG_SHOW_ERROR_FC(APL_T("Ошибка при декодировании строки из буфера: %s."), (LPCTSTR)ErrMess);

			#endif
		}
#else // #ifdef _UNICODE
		if(dbuf.m_ansi_string)
		{
			if(bUseExternalString)
			{
				//value->sval=dbuf.m_data + dbuf.GetCurPos();
				value->SetExternalString(dbuf.m_data + dbuf.GetCurPos());
				dbuf.SetCurPos(dbuf.GetCurPos()+size);
			}
			else
			{
				value->sval=new char[size];
				dbuf.Read(value->sval,size);
			}
		}
		else
		{
			//APL_THROW_EX(APLAPIERR_INCOMPATIBLDATAFORMAT,CString( APL_T("Для работы с буфером нужна UNICODE версия")));
			CaplStringAdapter str_ad((const wchar_t*)(dbuf.m_data + dbuf.GetCurPos()));		
			value->Set((const char*)str_ad);
			dbuf.SetCurPos(dbuf.GetCurPos()+size*sizeof(wchar_t));
		}

#endif // #ifdef _UNICODE
		value->type=aplSTRING;
		break;
	case aplINTEGRATEDBIN: 
		dbuf.Read(&ival,sizeof(ival));
		CaplIntegrBINVal blobval;blobval.blobId=ival;
		value->Set(blobval); break;
	case aplBINARY:
		dbuf.Read(&size);
		//********sval=new char[size];
		//*******dbuf.Read(sval,size);
		//********databuf.Add(sval,size);
		//********value->Set(databuf);
		//****delete sval;
		break;
	case aplAGGR: 
		{
			CaplAggr aggrval;
			CaplValue val;
			dbuf.Read(&size);
			for(i=0;i<size;i++)
			{
				val.Clear();
				aplReadValueFromBuf(dbuf,instmap, &val, bUseExternalString);
				aggrval.Add(val);
			}
			value->Set(aggrval);
			aggrval.Clear();
		}
		break;
	case aplNOTYPE: break; 
	case aplBLOB: break;
	}
	return true;
}
//**********************************************************************

bool CaplStepData::SaveToDataBuf(CaplDataBuf &dbuf,int ver)
{
	SetLastError(0);
	int i,j;
	USHORT type_id_b,attr_id_b;
	CString buf,buf1;
	CaplInstance *inst_b;

	//Пытаемся убрать мега тормоза.
	//Подменяем карту на STL и избавляемся вовсе от ненужного массива с проверкой уникальности при каждом ADD
//	CaplMap inst_map;//inst_map.UniqueIn=false;

	aplExtent ext_writed; ext_writed.Unique=false;  ext_writed.SetSize(10000);

#ifdef USE_CAPLMAP_MAP_IN_SAVE_TO_DATA_BUF
	
	CaplMap inst_map;
	inst_map.SetSize(instances.GetSize()+3);
#else
	TInstIntMap inst_map;
	TInstIntMap_it it;
#endif

#ifdef _UNICODE
	// заголовок буфера данных всегда однобайтовый - чтобы его могла прочитать любая версия
	if(m_global_ansi_string)
	{
		if(ver==0x0003){
			dbuf.Add(apl_step_format_id_3,strlen(apl_step_format_id_3)+1);
		}else{
			dbuf.Add(apl_step_format_id_2,strlen(apl_step_format_id_2)+1);
		}
	}
	else
	{
		if(ver==0x0003){
			dbuf.Add(apl_step_format_id_u3,strlen(apl_step_format_id_u3)+1);
		}else{
			dbuf.Add(apl_step_format_id_u2,strlen(apl_step_format_id_u2)+1);
		}
	}
#else // #ifdef _UNICODE
	if(ver==0x0003){
		dbuf.Add(apl_step_format_id_3,strlen(apl_step_format_id_3)+1);
	}else{
		dbuf.Add(apl_step_format_id_2,strlen(apl_step_format_id_2)+1);
	}
#endif // #ifdef _UNICODE

#ifdef _UNICODE
	// в юникодовой версии выбираем кодировку строк в буфере
	dbuf.m_ansi_string = m_global_ansi_string;
#endif // #ifdef _UNICODE
	// имя схемы как и остальные строки - зависит от формата/заголовка буфера

	UINT32 k=instances.Size;

	// Учет удаленных и временных
	for(i=0;i<instances.Size;i++){
		if (instances[i]==0) {k--;continue;}
		if (instances[i]->GetType()==0) {k--;continue;}
		if (instances[i]->GetTemporary()) k--;// 14.03.2017
	} // 05.10.2000

	int size4setsize=256+150*k;  // 150 взято из примера реальных данных
	if(size4setsize>dbuf.GetMaxSize()) 	dbuf.SetSize(size4setsize);

	dbuf.AddStrBuf(m_CurSchema);

	QT_THREAD_ENTER_CS
	UINT32 ui_MaxInstNum=(UINT32)m_MaxInstNum;
	QT_THREAD_LEAVE_CS

	dbuf.Add(&ui_MaxInstNum,sizeof(ui_MaxInstNum));
	dbuf.Add(&k,sizeof(k));

	// Запись Instance
	UINT32 i_inst=0;
	for(i=0;i<instances.Size;i++)
	{
		inst_b=instances[i];
		if (inst_b==0) continue;
		if (inst_b->GetType()==0) continue;
		if(inst_b->GetTemporary()) continue;// 16.01.2003

		UINT32 id_b=inst_b->GetId();
		dbuf.Add(&id_b,sizeof(id_b));

		type_id_b=(USHORT)inst_b->GetType()->id;
		dbuf.Add(&type_id_b,sizeof(type_id_b));

		BYTE access_mode=inst_b->GetAccessmode();
		dbuf.Add(&access_mode,sizeof(access_mode));

		ext_writed.Add(inst_b);

#ifdef USE_CAPLMAP_MAP_IN_SAVE_TO_DATA_BUF

		int old_size=inst_map.GetSize();
		inst_map.Add(inst_b, i_inst);
		if(old_size!=inst_map.GetSize()) i_inst++;
#else
		if(inst_map.insert(std::make_pair(inst_b, i_inst)).second==true)
			i_inst++;
#endif

		if(i%1000==0)
			PumpTimerPaintStepData(m_dwLastPaintTickCount);
	}

	// Запись атрибутов
	//тут делаем проход по карте

// 	for(it=inst_map.begin(); it!=inst_map.end(); ++it)
// 	{
// 		inst_b=it->first;
// 		i_inst = it->second;

	// ЯАИ Переделал чтобы объекты сохранялись всегда в одном порядке (надо для работы со слепками)
	for(i=0;i<ext_writed.Size;i++)
	{
		inst_b=ext_writed[i];

#ifdef USE_CAPLMAP_MAP_IN_SAVE_TO_DATA_BUF

		i_inst=inst_map.QGetByIn(inst_b);
		ASSERT(-1!=(INT32)i_inst);
#else
		TInstIntMap_it it = inst_map.find(inst_b);
		if(it!=inst_map.end())
			i_inst=it->second;
		else
			i_inst = 0;
#endif

		dbuf.Add(&i_inst,sizeof(i_inst));
		CaplEntity *ent=inst_b->GetType();

		if(inst_b->attrs==0 || ent==0) // нет атрибутов или инстанс удалена
		{
			k=0;
			dbuf.Add(&k,sizeof(k));
			continue;
		}

		k=0;
		for(j=0;j<ent->all_attrs.Size;j++) // Считаем сколько атрибутов
		{
			if(inst_b->attrs[j].value.type!=aplNOTYPE) k++; 
		}
		dbuf.Add(&k,sizeof(k));

		for(j=0;j<ent->all_attrs.Size;j++)
		{
			if(inst_b->attrs[j].value.type==aplNOTYPE ) continue;

			attr_id_b=(USHORT)ent->all_attrs[j]->id;
			dbuf.Add(&attr_id_b,sizeof(attr_id_b));

#ifdef USE_CAPLMAP_MAP_IN_SAVE_TO_DATA_BUF

			aplAppendBufValue(dbuf,inst_map,&(inst_b->attrs[j].value));
#else
			aplAppendBufValue(dbuf,&inst_map,&(inst_b->attrs[j].value));
#endif
		}
		PumpTimerPaintStepData(m_dwLastPaintTickCount);
	}
	return true;
}
//**********************************************************************
void aplAddInstValToExtent(aplExtent &ext,CaplValue *value)
{
	if(value==0) return;
	if(value->type==aplINSTANCE)
	{
		if(value->instval!=0)
		{
			if(value->instval->GetType()!=0 && !value->instval->GetTemporary())
			{
				ext.Add(value->instval);
			}
		}
	}
	else if(value->type==aplAGGR)
	{
		if(value->aggrval!=0)
		{
			for(int i=0;i<value->aggrval->GetSize();i++)
			{
				aplAddInstValToExtent(ext,value->aggrval->GetByIndex(i));
			}
		}
	}
}
//**********************************************************************
bool CaplStepData::SaveChangesToDataBuf(CaplDataBuf &dbuf)
{
	SetLastError(0);
	int i,j;
	UINT32 id_b;
	USHORT type_id_b,attr_id_b;
	CString buf,buf1;
	CaplInstance *inst_b;

#ifdef USE_CAPLMAP_MAP_IN_SAVE_TO_DATA_BUF
	CaplMap inst_map;
	inst_map.SetSize(10*1024);
#else
	TInstIntMap inst_map;
	TInstIntMap_it it;
#endif	

	aplExtent eKilledInst;
	eKilledInst.Unique=false;
	UINT32 i_inst=0;
	aplExtent ext;
	// получение списка сохраняемых Instance
	for(i=0;i<instances.Size;i++)
	{
		inst_b=instances[i];
		bool added=false;
		if(inst_b==0) continue; // 14.03.2011
		if(inst_b->GetTemporary()) continue;// 16.01.2003
		CaplEntity *ent(inst_b->GetType());
		if(ent==0 && inst_b->IsDeleted()) {eKilledInst.Add(inst_b); continue;}
		
		if(inst_b->GetId()==0)
		{
#ifdef USE_CAPLMAP_MAP_IN_SAVE_TO_DATA_BUF

			int old_size=inst_map.GetSize();
			inst_map.Add(inst_b, i_inst);
			if(old_size!=inst_map.GetSize()) i_inst++;
#else
			if(inst_map.insert(std::make_pair(inst_b, i_inst)).second==true)
				i_inst++;
#endif
			added=true;
		}

		if(ent!=0 && inst_b->attrs!=0)
		{
			for(j=0;j<ent->all_attrs.Size;j++)
			{
				if(inst_b->attrs[j].changed==true)
				{
					if(!added)
					{
#ifdef USE_CAPLMAP_MAP_IN_SAVE_TO_DATA_BUF

						int old_size=inst_map.GetSize();
						inst_map.Add(inst_b, i_inst);
						if(old_size!=inst_map.GetSize()) i_inst++;
#else

						if(inst_map.insert(std::make_pair(inst_b, i_inst)).second==true)
							i_inst++;
#endif
						added=true;
					}
					// дополнение теми, на кого ссылается
					ext.Clear();
					aplAddInstValToExtent(ext, &(inst_b->attrs[j].value));
					for(int j=0; j<ext.Size; ++j)
					{
						CaplInstance *inst_j=ext[j];
						if(inst_j==NULL) continue;
						if(inst_j->GetType()==NULL) continue;
						if(inst_j->GetTemporary()) continue;

#ifdef USE_CAPLMAP_MAP_IN_SAVE_TO_DATA_BUF
						int old_size=inst_map.GetSize();
						inst_map.Add(inst_j, i_inst);
						if(old_size!=inst_map.GetSize()) i_inst++;
#else
						if(inst_map.insert(std::make_pair(inst_j, i_inst)).second==true)
							i_inst++;
#endif
					}
				}
			}
		}

		PumpTimerPaintStepData(m_dwLastPaintTickCount);
	}

#ifdef _UNICODE
	// заголовок буфера данных всегда однобайтовый - чтобы его могла прочитать любая версия
	if(m_global_ansi_string)
	{
		// если работаем с ансишным файлом, кешем - то и заголовок должен быть ансишным
		dbuf.Add(apl_step_format_id_2,strlen(apl_step_format_id_2)+1);
	}
	else
	{
		dbuf.Add(apl_step_format_id_u2,strlen(apl_step_format_id_u2)+1);
	}
#else // #ifdef _UNICODE
	dbuf.Add(apl_step_format_id_2,strlen(apl_step_format_id_2)+1);
#endif // #ifdef _UNICODE

#ifdef _UNICODE
	// в юникодовой версии выбираем кодировку строк в буфере
	dbuf.m_ansi_string = m_global_ansi_string;
#endif // #ifdef _UNICODE
	// имя схемы как и остальные строки - зависит от формата/заголовка буфера

#ifdef USE_CAPLMAP_MAP_IN_SAVE_TO_DATA_BUF

	UINT32 k=inst_map.GetSize()+eKilledInst.Size;
#else
	UINT32 k=inst_map.size()+eKilledInst.Size;
#endif
	int size4setsize=256+150*k;  // 150 взято из примера реальных данных
	if(size4setsize>dbuf.GetMaxSize()) 	dbuf.SetSize(size4setsize);

	dbuf.AddStrBuf(m_CurSchema);

	QT_THREAD_ENTER_CS
	UINT32 ui_MaxInstNum=m_MaxInstNum;
	QT_THREAD_LEAVE_CS
	dbuf.Add(&ui_MaxInstNum,sizeof(ui_MaxInstNum));
	dbuf.Add(&k,sizeof(k));

	// Запись Instance
#ifdef USE_CAPLMAP_MAP_IN_SAVE_TO_DATA_BUF
	inst_map.SortIn();
	for(i=0;i<inst_map.GetSize();i++)
	{
		inst_b=(CaplInstance*)inst_map.Data[i].in;

#else
	for(it=inst_map.begin();it!=inst_map.end(); ++it)
	{
		inst_b=it->first;
#endif
		id_b=inst_b->GetId();
		type_id_b=(USHORT)inst_b->GetType()->id;
		dbuf.Add(&id_b,sizeof(id_b));
		dbuf.Add(&type_id_b,sizeof(type_id_b));
		BYTE access_mode=inst_b->GetAccessmode();
		dbuf.Add(&access_mode,sizeof(access_mode));

		if(i%1000==0)
			PumpTimerPaintStepData(m_dwLastPaintTickCount);
	}

	// Запись в структуру удаленных
	for(i=0;i<eKilledInst.Size;i++)
	{
		id_b=eKilledInst[i]->GetId();
		type_id_b=(USHORT)0;
		dbuf.Add(&id_b,sizeof(id_b));
		dbuf.Add(&type_id_b,sizeof(type_id_b));
		// DIV
		BYTE access_mode=eKilledInst[i]->GetAccessmode(); 
		dbuf.Add(&access_mode,sizeof(access_mode));
		if(i%1000==0) PumpTimerPaintStepData(m_dwLastPaintTickCount);
	}

	// Запись атрибутов

#ifdef USE_CAPLMAP_MAP_IN_SAVE_TO_DATA_BUF
	for(i=0;i<inst_map.GetSize();i++)
	{
		inst_b=(CaplInstance*)inst_map.Data[i].in;
#else
	for(it=inst_map.begin();it!=inst_map.end(); ++it)
	{
		inst_b=it->first;
#endif
		if(inst_b==0) continue;
		CaplEntity *ent=inst_b->GetType();
		if (0==ent) continue;

#ifdef USE_CAPLMAP_MAP_IN_SAVE_TO_DATA_BUF
		
		i_inst=(UINT32)inst_map.Data[i].out;
		ASSERT(-1!=(INT32)i_inst);
#else
		TInstIntMap_it fit(inst_map.find(inst_b));
		if(fit!=inst_map.end())
			i_inst=fit->second;
		else
		{
			i_inst = 0;
			ASSERT(i_inst);
		}
#endif
		dbuf.Add(&i_inst,sizeof(i_inst));

		k=0;
		
		for(j=0;j<ent->all_attrs.Size;j++) 
		{
			if(inst_b->attrs[j].changed==true)
				k++;
		}
		dbuf.Add(&k,sizeof(k));

		for(j=0;j<ent->all_attrs.Size;j++) 
		{
			if(inst_b->attrs[j].changed==true)
			{
				attr_id_b=(USHORT)ent->all_attrs[j]->id;
				dbuf.Add(&attr_id_b,sizeof(attr_id_b));
#ifdef USE_CAPLMAP_MAP_IN_SAVE_TO_DATA_BUF
				aplAppendBufValue(dbuf, inst_map, &(inst_b->attrs[j].value));
#else
				aplAppendBufValue(dbuf, &inst_map, &(inst_b->attrs[j].value));
#endif
				inst_b->attrs[j].changed=false;
			}
		}
		if(i%1000==0) PumpTimerPaintStepData(m_dwLastPaintTickCount);
	}
	return true;
}

void aplAppendBufValue(CaplDataBuf &data, CaplMap &inst_map, CaplValue *value)
{
	aplValueType type=aplNOTYPE;
	UINT32 i, size, instid;
	CaplValue *val;
	if(value==0){data.Add(&type,sizeof(type));return;}
	//if(instmap==0) {data.Add(&type,sizeof(type));return;}
	type=value->type;
	BYTE microtype=(BYTE)type;
	data.Add(&microtype,sizeof(microtype));
	switch (type)
	{
	case aplINTEGER: data.Add(&(value->ival),sizeof(value->ival)); break;
	case aplREAL: data.Add(&(value->rval),sizeof(value->rval)); break;
	case aplBOOL: data.Add(&(value->bval),sizeof(value->bval)); break;
	case aplLOGICAL: data.Add(&(value->lval),sizeof(value->lval)); break;
	case aplINSTANCE:
	case aplSELECT:
		{
		instid=inst_map.QGetByIn(value->instval);
		// if((INT32)instid==-1) instid=0;
		data.Add(&instid,sizeof(instid)); 
		}
		break;
	case aplSTRING: 
	case aplENUMERATION:
		if(value->sval==0) value->Set(_T(""));
		size = _strlen(value->sval)+1;
#ifdef _UNICODE
		// добавил возможность сохранить U строку в ansi буфер. Вдруг понадобится
		// хотя что она там наконвертирует...
		if(data.m_ansi_string)
		{
			CaplStringAdapter str_ad(value->sval);
			#ifndef _MFC_VER
				str_ad.m_bIsAnsiUtf8=false; // В буфере либо 1251, либо UCS2
			#endif
			data.Add(&size,sizeof(size));
			data.Add((void*)(const char*)str_ad,size); 
			break;
		}
		else
		{
			#ifndef __linux__
				ASSERT(2==sizeof(TCHAR));
				data.Add(&size,sizeof(size));
				data.Add(value->sval,size*sizeof(TCHAR)); 
			#else
				ASSERT(4==sizeof(TCHAR));
				BYTE *strUcs2=0; 
				int strUcs2Len=0;
				CString ErrMess(_T(""));
				if(bConvertTextWithIconv(aplUTF32LE,(BYTE*)value->sval,size*sizeof(TCHAR),aplUCS2,strUcs2,strUcs2Len, 0, &ErrMess))
				{
					if(0!=strUcs2)
					{
						data.Add(&size,sizeof(size));
						data.Add(strUcs2,strUcs2Len);
						delete[] strUcs2;
					}
					else
					{
						size = 0;
						data.Add(&size, sizeof(size));
					}
				}
				else
				{
					if(!ErrMess.IsEmpty())
						LOG_SHOW_ERROR_FC(APL_T("Ошибка при кодировании строки для записи в буфер: %s. Некорректная строка: %s"),
										  (LPCTSTR)ErrMess, (LPCTSTR)value->sval);
					size = 0;
					data.Add(&size, sizeof(size));
				}

			#endif
			break;
		}
#else // #ifdef _UNICODE
		if(data.m_ansi_string)
		{
			data.Add(&size,sizeof(size));
			data.Add(value->sval,size); 
		}
		else
		{
			//APL_THROW_EX(APLAPIERR_INCOMPATIBLDATAFORMAT,CString( APL_T("Для работы с буфером нужна UNICODE версия")));
			CaplStringAdapter str_ad(value->sval);
			CStringW str_w = (const wchar_t*)str_ad;
			data.Add(&size,sizeof(size));
			data.Add(str_w.GetBuffer(),size*2); 
		}
#endif // #ifdef _UNICODE
		break;
	case aplINTEGRATEDBIN: 
		data.Add(&(value->intbinval->blobId),sizeof(value->intbinval->blobId)); 
		break;
	case aplBINARY: 
		size=value->binval->m_Size;
		data.Add(&size,sizeof(size));
		data.Add(value->binval->m_data,size); break;
	case aplAGGR: 
		size=value->aggrval->GetSize();
		data.Add(&size,sizeof(size));
		for(i=0;i<size;i++)
		{
			val=value->aggrval->GetByIndex(i);
			aplAppendBufValue(data,inst_map,val);
		}
		break;
	case aplBLOB: break; 
	case aplNOTYPE :break;
	}
}
//**********************************************************************
bool CaplStepData::ApploadFromDataBuf(CaplDataBuf &dbuf,aplExtent *ext_inst)
{
	QT_THREADS_DENY_FROM_READ
	bool b_ext_inst_unique=true;
	
	if(ext_inst!=0)
	{
		ext_inst->Clear();
		b_ext_inst_unique=ext_inst->Unique;
		ext_inst->Unique=false;
	}

	SetLastError(0);
	bool tmp_readonly;
	UINT32 i, k=0;
	UINT32	inst_size=0;
	UINT32 id_b;
	USHORT type_id_b,attr_id_b;
	CString buf,buf1;
	CaplInstance *inst,*lastinst;
	CaplEntity *ent,*lastent;
	CaplAttr *attr;
	pCaplInstance *instmap=0;
	
	CaplValue value;

	// буфер для версии буфера, должен быть однобайтовой строкой
	char version[20];
	//!!!!!!
	int last_size_inst=0;
	int memory_error=0;
	if(dbuf.m_Size==0) return false;

	bool prevTestAccessMode = m_TestAccessMode;
	bool myCatch = false;

#ifdef _MFC_VER

start_appload:	

#endif
	
	try
	{
		if(!SaplErrorDescription::GetRetranslateMemoryExeption())
		{
			myCatch = true;
			SaplErrorDescription::SetRetranslateMemoryExeption();
		}

		dbuf.Reset();
		// все должно быть отсортировано по id
		CSortClass::SortInstById(instances);
#ifdef _DEBUG
//		SaveToFile("D:\\000.std");
#endif
		k=0;
#ifdef _MFC_VER
		_heapmin();
#endif
		dbuf.Read(version,strlen(apl_step_format_id_2)+1);
#ifdef _UNICODE

		if(CStringA(apl_step_format_id_u2) != version && CStringA(apl_step_format_id_u3) != version)
		{
			if(CStringA(apl_step_format_id_2) == version || CStringA(apl_step_format_id_3) == version)
			{
				// буфер с однобайтовыми строками
				dbuf.m_ansi_string = true;
				m_global_ansi_string = true;
			}
			else
			{
				//MessageBox(0, APL_T("Не тот формат"), APL_T("Ошибка"),MB_ICONERROR);
				if(CStringA(version).Find(apl_step_format_mask,0) == 0)
				{
					CaplStringAdapter str_ad((const char*)version);
					APL_THROW_EX(APLAPIERR_INCOMPATIBLDATAFORMAT, CString( APL_T("Версия файла: ")) + CString((LPCTSTR)str_ad).Mid(15));
				}
				else
				{
					APL_THROW(APLAPIERR_INVALIDDATAFORMAT);
				}
			}
		}
		else
		{
			dbuf.m_ansi_string = false;
			m_global_ansi_string = false;
		}
#else // #ifdef _UNICODE
		if(CStringA(apl_step_format_id_2)!=version && CStringA(apl_step_format_id_3)!=version)
		{
			//MessageBox(0,"Не тот формат","Ошибка",MB_ICONERROR);
			if(CStringA(version).Find(apl_step_format_id_u2,0)==0)
			{
				//APL_THROW_EX(APLAPIERR_INCOMPATIBLDATAFORMAT,CString( APL_T("Файл содержит Unicode строки и не может быть прочитан. Используйте версию для unicode.")));
				dbuf.m_ansi_string = false;
				m_global_ansi_string = false;
			}
			else if(CStringA(version).Find(apl_step_format_mask,0)==0)
			{
				APL_THROW_EX(APLAPIERR_INCOMPATIBLDATAFORMAT,CString( APL_T("Версия файла: "))+CString(version).Mid(15));
			}
			else
			{
				APL_THROW(APLAPIERR_INVALIDDATAFORMAT);
			}
		}
#endif // #ifdef _UNICODE
		CString ver_buf;
		// имя схемы как и остальные строки - зависит от формата/заголовка буфера
		dbuf.ReadStrBuf(ver_buf);

		if(m_CurSchema!=ver_buf)
		{
			if(instances.Size>0)APL_THROW(APLAPIERR_INCOMPATIBLEDICT);
			CString tmp_name=m_CurDataFile;
			ClearDict();
			if(!LoadDictionary(ver_buf)) 
			{
#ifndef _MFC_VER
				if(m_MultyThreads)
				{
					SThreadContext* pErr = GetErrContext();
					if(pErr!=0)
					{
						if(pErr->m_LastErrorCode==APLAPIERR_NOERROR)
							APL_THROW_EX(APLAPIERR_BADDICTIONARY, pErr->m_ErrorDescription);
						else
							APL_THROW_EX(pErr->m_LastErrorCode, pErr->m_ErrorDescription);
					}
					else
						APL_THROW(APLAPIERR_BADDICTIONARY);
				}
#endif
				//MessageBox(0,"Кривой словарик","Ошибка",MB_ICONERROR);
				if(m_LastErrorCode==APLAPIERR_NOERROR)
					APL_THROW_EX(APLAPIERR_BADDICTIONARY, m_ErrorDescription);
				else
					APL_THROW_EX(m_LastErrorCode, m_ErrorDescription);
			}
			m_CurDataFile=tmp_name;
		}

		UINT32 ui_MaxInstNum;
		dbuf.Read(&ui_MaxInstNum,sizeof(ui_MaxInstNum));
		QT_THREAD_ENTER_CS
		m_MaxInstNum=ui_MaxInstNum;
		QT_THREAD_LEAVE_CS
		dbuf.Read(&inst_size);
//		instances.SetSize(k+instances.Size); Вредная строка, жутко увеличивает фрагментацию памяти
		instmap=new pCaplInstance[inst_size];
		for(i=0;i<inst_size; i++) instmap[i]=0;
		//!!!!
		last_size_inst=instances.Size;

		if(ext_inst!=0) ext_inst->SetSize(inst_size);

		// чтение Instance
		m_TestAccessMode=false; //временно отключаем проверку доступа
		for(i=0;i<inst_size;i++)
		{
			if(!dbuf.Read(&id_b)) 
				APL_THROW(APLAPIERR_BADDATA);
			if(!dbuf.Read(&type_id_b,sizeof(type_id_b))) 
				APL_THROW(APLAPIERR_BADDATA);
			BYTE access_mode=0;
			if(!dbuf.Read(&access_mode,sizeof(access_mode))) 
				APL_THROW(APLAPIERR_BADDATA);

			// поиск соответствующего
			inst=0;
			if(m_is_readonly && access_mode<2){access_mode=2;}
			if(id_b!=0)
			{
				QT_THREAD_ENTER_CS
				if(id_b>m_MaxInstNum){
					m_MaxInstNum=id_b+1;
				}
				QT_THREAD_LEAVE_CS

				if(last_size_inst>0){
					aplQFindInstIdInExtent(*((aplExtent*)(&instances)),id_b,last_size_inst,false,&inst);
				}
			}
			if(type_id_b==0)
			{
				// удаление Instance. Критически важно что третий флаг true! (DIV)
				if(inst!=0) { if(inst->GetType()!=0) DeleteInstance(inst,true,true); }
			}
			else
			{
				ent=GetEntityById(type_id_b);
				if(0==ent)
				{
					if(type_id_b>cMinId4ExtSchema)
					{
						LoadDictionaryAddition(type_id_b);
						ent=GetEntityById(type_id_b);
					}
					if(0==ent)
					{
						buf.Format(_T(" Id entity: %i"),type_id_b);
						APL_THROW_EX(APLAPIERR_BADDATA_INVALID_ENTINY_ID, buf);
					}
				}

				// создание нового
				if(0==inst)
				{
					// проверка в удаленных
					/* убрано 05.10.200
					if(killed_instances.Find(id_b)!=-1)
					{
						id_b=id_b;
					}
					if(killed_instances.Find(id_b)==-1)*/
					{
						tmp_readonly=m_is_readonly;
						m_is_readonly=false;
						inst=CreateInstance(ent,false,false);
						inst->SetId(id_b);
						m_is_readonly=tmp_readonly;
					}
				}
				inst->SetAccessmode((aplAccessModeType)access_mode);
				instmap[i]=inst;
				if(ext_inst!=0) ext_inst->Add(inst);
			}
			PumpTimerPaintStepData(m_dwLastPaintTickCount);
		}
		CSortClass::SortInstById(instances);
#ifdef _DEBUG
//		SaveToTextFileDebug(*this,_T("D:\\333.txt"));
#endif

		id_b = 0;
		lastinst = 0;
		//Чтение attributes

		while (dbuf.Read(&id_b))
		{
			if(id_b==(UINT32)-1)
				break;

			if(id_b >= inst_size)
			{
				CString str;str.Format(APL_T("Запрошен слишком большой индекс инстанса в массиве (%i из %i)"), id_b, inst_size);
				APL_THROW_EX(APLAPIERR_BADINSTANCE, str);
			}

			lastinst=instmap[id_b];
			//if((long)lastinst==0) 
			//	APL_THROW(APLAPIERR_BADDATA);
			if(0==lastinst)
			{
				dbuf.Read(&k);
				for(i=0;i<k;i++)
				{
					if(!dbuf.Read(&attr_id_b,sizeof(attr_id_b)))APL_THROW(APLAPIERR_BADDATA);
					aplReadValueFromBuf(dbuf,instmap,&value,true);
				}
				continue;
			}
			lastent=lastinst->GetType();
			dbuf.Read(&k);
			for(i=0;i<k;i++)
			{
				if(!dbuf.Read(&attr_id_b,sizeof(attr_id_b)))
					APL_THROW(APLAPIERR_BADDATA);
				if(attr_id_b==0) continue;
				aplReadValueFromBuf(dbuf,instmap,&value,true);
				
				if(lastent==0) continue; // Inst удален в текущей сессии
				
				attr=GetAttrDefinitionById(attr_id_b);			
				
				if(0==attr)
				{

					if(attr_id_b>cMinId4ExtSchema)
					{
						LoadDictionaryAddition(attr_id_b);
						attr=GetAttrDefinitionById(attr_id_b);
					}
					if(0==attr)
					{
						buf.Format(_T(" (%i)\n\nInstance ID = %i\n\nEntity ID = %i (%s)"),
							attr_id_b,lastinst->GetId(),lastent->id, LPCTSTR(lastent->name));
						APL_THROW_EX(APLAPIERR_BADDATA_INVALID_ATTR_ID, buf);
					}
				}
				// Проверка принадлежности атрибута entity
				if(!IsEntityAttr(lastent,attr))
				{
					buf.Format(_T(" (%i)\n\nInstance ID = %i\n\nEntity ID = %i (%s)"),
						attr_id_b,lastinst->GetId(),lastent->id, LPCTSTR(lastent->name));
					APL_THROW_EX(APLAPIERR_BADDATA_INVALID_ATTR_ID,buf);
				}

				CaplInstance::CaplValueDefinition *val=GetAttrValue(lastinst,attr,true);
				//CaplInstance::CaplValueDefinition *val=GetAttrValueByIndex(lastinst,attr_index);
				if(val==0)
				{
					APL_THROW(APLAPIERR_BADDATA);
					continue;
				}
				if(val->changed==false)
				{
					if(ChekAttrType(lastinst,attr,value))
					{
						val->value.Set(value);
						if(value.type==aplAGGR)
						{
							if(val->ArhiveAggr==0) val->ArhiveAggr=new CaplAggr;
							else val->ArhiveAggr->Clear();
							val->ArhiveAggr->Append(*(value.aggrval));
						}
					}
				}				
			}
			PumpTimerPaintStepData(m_dwLastPaintTickCount);
		}
		m_TestAccessMode=prevTestAccessMode; //снова включаем проверку доступа
	}
#ifdef _MFC_VER

	catch(CMemoryException* e)
	{
		TCHAR   szCause[255];  szCause[0]=_T('\0');

		e->GetErrorMessage(szCause,255);
		if(0==memory_error){
			_heapmin();
			memory_error++;
			goto start_appload;
		}

		CString err_mess;
		err_mess.Format(APL_T("Ошибка: %s\n k: %i instances.Size: %i"),LPCTSTR(szCause),k,instances.GetSize());

		m_ErrorAnnotation = err_mess;
		SetLastError(APLAPIERR_OUT_OF_MEMORY);

		int err=m_LastErrorCode;
		CString err_descr=m_ErrorDescription;
		ClearData();
		m_LastErrorCode=err;
		m_ErrorDescription=err_descr;
		if (instmap!=0) delete []instmap;
		if(ext_inst!=0) ext_inst->Unique=b_ext_inst_unique;		

		m_TestAccessMode=prevTestAccessMode; //снова включаем проверку доступа
		
		if(SaplErrorDescription::GetRetranslateMemoryExeption())
		{
			if(myCatch)SaplErrorDescription::SetRetranslateMemoryExeption(false);
			else APL_THROW_EX(27,err_mess);
		}
		aplMessageBox(err_mess, MB_OK|MB_ICONERROR);
		return false;
	}
#endif
	catch(SaplErrorDescription error)
	{
		if(	m_ClearDataAfterError)ClearData();
		if (instmap!=0) delete []instmap;
		if(ext_inst!=0) ext_inst->Unique=b_ext_inst_unique;
		m_TestAccessMode=prevTestAccessMode; //снова включаем проверку доступа

		if(SaplErrorDescription::GetRetranslateMemoryExeption())
		{
			if(myCatch)SaplErrorDescription::SetRetranslateMemoryExeption(false);
			else
			{
#ifndef _MFC_VER
				if(m_MultyThreads)
				{
					SThreadContext* pErr = GetErrContext();
					if(pErr!=0)
					{
						if(!error.m_err_descr.IsEmpty() && !pErr->m_ErrorAnnotation.IsEmpty())error.m_err_descr += _T("\n");
						error.m_err_descr += pErr->m_ErrorAnnotation; pErr->m_ErrorAnnotation.Empty();
					}
					throw(SaplErrorDescription(error,_T(__FILE__),_T(__DATE__),__LINE__,0,__APL_FUNC__));
				}
#endif
				if(!error.m_err_descr.IsEmpty() && !m_ErrorAnnotation.IsEmpty())error.m_err_descr += _T("\n");
				error.m_err_descr += m_ErrorAnnotation; m_ErrorAnnotation.Empty();
				throw(SaplErrorDescription(error,_T(__FILE__),_T(__DATE__),__LINE__,0,__APL_FUNC__));
			}
		}
		if(error.m_err_code!=-1)SetLastErrorEx(error);
		return false;
	}
	catch(...)
	{
		SThreadContext ErrCtx;
		ErrWrite2Ctx(&ErrCtx);

		if(	m_ClearDataAfterError)ClearData();

		ErrReadFromCtx(&ErrCtx);

		if (instmap!=0) delete []instmap;

		if(ext_inst!=0) ext_inst->Unique=b_ext_inst_unique;

		m_TestAccessMode=prevTestAccessMode; //снова включаем проверку доступа

		return false;
	}
	if (instmap!=0) delete []instmap;

	if(ext_inst!=0) ext_inst->Unique=b_ext_inst_unique;
	
#ifndef _MFC_VER
	if(0==memory_error){}; // Тупо чтобы не ругался gcc
#endif

	return true;
}
//**********************************************************************
bool CaplStepData::SaveToFile(LPCTSTR file_name, bool changes_only)
{
	if(aplIsStringEmpty(file_name) && m_CurDataFile==_T("")) {SetLastErrorWithFileInfo(APLAPIERR_BADFILENAME); return false;}
	SetLastError(0);

	CString CurrentSave;
	if(file_name!=0)
	{
		CurrentSave = file_name;
	}
	else
	{
		CurrentSave = m_CurDataFile;
	}
	CString tmp_file=CurrentSave;
	tmp_file+=_T(".tmp");
	CString bak_file=CurrentSave;
	bak_file+=_T(".bak");
	CString bak_file_d=bak_file+_T(".tmp");


	if(CaplFile::IsFileExist(CurrentSave))	
	{
		if(!CaplFile::IsFileAttrAllowWrite(CurrentSave))
		{
			SetLastErrorWithFileInfoExtText(APLAPIERR_FILE_BD_READ_ONLY,CurrentSave);return false;
		}
	}
	
	if(CaplFile::IsFileExist(bak_file))	
	{
		if(!CaplFile::IsFileAttrAllowWrite(bak_file))
		{
			SetLastErrorWithFileInfoExtText(APLAPIERR_FILE_BD_READ_ONLY,bak_file);return false;
		}
	}

	if(CaplFile::IsFileExist(tmp_file))	
	{
		if(!CaplFile::IsFileAttrAllowWrite(tmp_file))
		{
			SetLastErrorWithFileInfoExtText(APLAPIERR_FILE_BD_READ_ONLY,tmp_file);return false;
		}
	}


	CaplDataBuf data;
	if(changes_only)
	{
		if(!SaveChangesToDataBuf(data)) return false;
	}
	else
	{
		if(m_Default_TextFormat) return SaveToTextFile(CurrentSave);
		if(!SaveToDataBuf(data)) return false;
	}

	CFileException pError;
	CFile file;
	if(!file.Open(tmp_file,CFile::modeCreate|CFile::modeWrite|CFile::typeBinary,&pError))
		{SetLastErrorWithFileInfoExtText(APLAPIERR_FILE_IO,tmp_file);return false;}
	
	bool err=false;
	TRY
	{
		file.Write(data.m_data,data.m_Size);
		file.Flush();
		file.Close();

		// Удаление старого bak 
		if(CaplFile::IsFileExist(bak_file))
		{
			// в два этапа - при антивирусной проверке удаление может тормозить в фоне, и тогда следющее переименование боевого файла в bak вернет ошибку
			// сначала переименовываем
			CFile::Rename(bak_file,bak_file_d);
			// потом удаляем
			CFile::Remove(bak_file_d);
		}

		// Сделать bak
		if(CaplFile::IsFileExist(CurrentSave))
			CFile::Rename(CurrentSave,bak_file);
		
		CFile::Rename(tmp_file,CurrentSave);

	}
#ifdef _MFC_VER
	CATCH( CFileException, e )
	{
		TCHAR szError[1024];
		e->GetErrorMessage(szError, 1024);
		m_ErrorAnnotation.Format(APL_T("При операциях с файлом '%s' возникла ошибка: %s"), (LPCTSTR)e->m_strFileName, (LPCTSTR)szError);
		err=true;
	}
#else
	catch(...)
	{
		err=true;
		CString sErr;
		#ifdef __linux__
			sErr = CaplFileException::Error2String(CFileException::ErrnoToException(errno));
		#else
			sErr = CaplFileException::Error2String(CFileException::OsErrorToException(_doserrno));
		#endif
		if(m_MultyThreads)
		{
			SThreadContext* pErr = GetErrContext();
			if(pErr!=0)
				pErr->m_ErrorAnnotation.Format(APL_T("При операциях с файлом '%s' возникла ошибка: %s"), (LPCTSTR)tmp_file, (LPCTSTR)sErr);
		}
		else m_ErrorAnnotation.Format(APL_T("При операциях с файлом '%s' возникла ошибка: %s"), (LPCTSTR)tmp_file, (LPCTSTR)sErr);
	}
#endif
	END_CATCH
	// создание bak
	if(err){SetLastErrorWithFileInfoExtText(APLAPIERR_FILE_IO,file_name);return false;}

	return true;
}
//**********************************************************************
bool CaplStepData::LoadFromFile(LPCTSTR file_name, bool appload_mode,bool check_ro)
{
	QT_THREADS_DENY_FROM_READ
	if(aplIsStringEmpty(file_name) && m_CurDataFile == _T("")) {SetLastErrorWithFileInfo(APLAPIERR_BADFILENAME); return false;}
	SetLastErrorWithFileInfo(APLAPIERR_NOERROR);
	if(file_name!=0){m_CurDataFile=file_name;}

	int internal_error=0;

	if(!CaplFile::IsFileExist(m_CurDataFile))	{
		SetLastErrorWithFileInfoExtText(APLAPIERR_FILE_BD_NOT_FOUND,m_CurDataFile);return false;
	}

	if(check_ro)
	{
		if(!CaplFile::IsFileAttrAllowWrite(m_CurDataFile)) internal_error=APLAPIERR_FILE_BD_READ_ONLY;


		CString bak_file=m_CurDataFile;
		bak_file+=_T(".bak");
		if(CaplFile::IsFileExist(bak_file))	
		{
			if(!CaplFile::IsFileAttrAllowWrite(bak_file)) internal_error=APLAPIERR_FILE_BD_READ_ONLY;
		}

		CString tmp_file=m_CurDataFile;
		tmp_file+=_T(".tmp");
		if(CaplFile::IsFileExist(tmp_file))	
		{
			if(!CaplFile::IsFileAttrAllowWrite(tmp_file)) internal_error=APLAPIERR_FILE_BD_READ_ONLY;
		}
	}

	CString tmp_name=m_CurDataFile;
	if(!appload_mode)ClearData();
	m_CurDataFile=tmp_name;

	
	CaplDataBuf dbuf;
	CFile file;
	CFileException pError;
	if(!file.Open(m_CurDataFile,CFile::modeRead,&pError)) 
	{
		if(pError.m_cause==CFileException::fileNotFound){
			//The file could not be located."
			SetLastErrorWithFileInfoExtText(APLAPIERR_BADFILENAME,m_CurDataFile); return false;
		}else if(pError.m_cause==CFileException::badPath ){
			//All or part of the path is invalid.
		}else if(pError.m_cause==CFileException::tooManyOpenFiles){
			//The permitted number of open files was exceeded.
		}else if(pError.m_cause==CFileException::accessDenied){
			//The file could not be accessed.
		}
		SetLastErrorWithFileInfoExtText(APLAPIERR_FILE_IO,m_CurDataFile); return false;
	}


	int mode=0;
	int i=strlen(apl_step_format_mask)+1;
	int k;
#if _MSC_VER >= 1400
	ULONGLONG size_f_l;
	size_f_l=file.GetLength();
	if(size_f_l>MAX_FILE_LENGTH_32_BIT)
	{
		return false;
	}
	else if(size_f_l<i)
	{
		SetLastErrorWithFileInfoExtText(APLAPIERR_FILE_IO,m_CurDataFile);
		return false;
	}
	k=(DWORD)size_f_l;
#else
	k=file.GetLength();
#endif

	if(k>i)
	{
		// заголовок однобайтовый, все операции в ansi
		char *c_buf=new char[i];
		file.Read(c_buf,i);
		c_buf[i-1]='\0';

		if(0==strcmp(c_buf,apl_step_format_mask))  mode=1;
		else if(0==strcmp(c_buf,apl_text_format_mask))  mode=2;
		else if(c_buf[0]=='{')mode=4;
		else 
		{
			c_buf[13]='\0';
			if(0==strcmp(c_buf,"ISO-10303-21;"))  mode=3;
		}
		delete[] c_buf;

	}

	if(0==mode) // Обработка на случай BOM в JSON
	{
		CString sFile(file_name);
		i=sFile.ReverseFind(_T('.'));
		if(i>0)
		{
			CString sExt=sFile.Right(sFile.GetLength()-i);
			sExt.MakeLower();
			if(sExt==_T(".json")) mode=4;
		}
	}



	if(mode==1)
	{
		file.Seek(0,CFile::begin);
		
		dbuf.SetSize(k);
		file.Read(dbuf.GetBuffer(),k);
		dbuf.m_Size=k;
		file.Close();
		m_Default_TextFormat=false;
		if(!ApploadFromDataBuf(dbuf))return false;
	}
	else if(mode==2)
	{
		file.Close();
		if(!LoadFromTextFile(m_CurDataFile)){SetLastErrorWithFileInfoExtText(APLAPIERR_INVALIDDATAFORMAT,m_CurDataFile);return false;}
	}
	else if(mode==3)
	{
		file.Close();
		aplP21Header header;
		if(!LoadP21(m_CurDataFile,header)){SetLastErrorWithFileInfo(APLAPIERR_INVALIDDATAFORMAT);return false;}
	}
	else if(mode==4)
	{
		file.Close();
		if(!LoadFromJsonFile(m_CurDataFile)){SetLastErrorWithFileInfoExtText(APLAPIERR_INVALIDDATAFORMAT,m_CurDataFile);return false;}
	}
	else
	{
		file.Close();
		return false;
	}
	
	if(GetLastError()!=APLAPIERR_NOERROR)return false;
	if(internal_error!=0)SetLastErrorWithFileInfoExtText(internal_error,m_CurDataFile);
	// Упорядочиваем для всяких целей...
	CSortClass::SortInstById(instances);

	return true;
}

//**********************************************************************
//**********************************************************************
//**********************************************************************
bool CaplStepData::TestCycleInRelationBN(LPCTSTR entity, LPCTSTR in_attr, LPCTSTR out_attr,
									   CaplInstance *in_val, CaplInstance *out_val)
{
	if(aplIsStringEmpty(entity)) return false;
	if(aplIsStringEmpty(in_attr)) return false;
	if(aplIsStringEmpty(out_attr)) return false;
	if(in_val==0) return false;
	if(in_val->GetType()==0) return false;
	if(out_val==0) return false;	
	if(out_val->GetType()==0) return false;	
	
	if(in_val==out_val) return false;
	
	CaplEntity *ent=GetEntityBN(entity);
	if(0==ent) return false;
	CaplAttr *attr1=0, *attr2=0;
	CString an1=in_attr;
	CString an2=out_attr;
	
	attr1=GetAttrDefinition(ent,an1);
	attr2=GetAttrDefinition(ent,an2);

	if((attr2==0)||(attr1==0)) return false;
	return TestCycleInRelation(ent,attr1,attr2,in_val,out_val);
}

bool CaplStepData::TestCycleInRelation(CaplEntity *entity, CaplAttr *in_attr, CaplAttr *out_attr,
									   CaplInstance *in_val, CaplInstance *out_val)
{
	if(0==entity) return false;
	if(0==in_attr) return false;
	if(0==out_attr) return false;
	if(0==in_val) return false;
	if(0==in_val->GetType()) return false;
	if(0==out_val) return false;
	if(0==out_val->GetType()) return false;
	
	if(in_val==out_val) return false;
	
	aplExtent ext0,ext1,ext2;
	ext2.Unique=true;
	int i,j;
	CaplInstance *inst0;

	GetEntityExtent(entity,ext0);

	ext1.Add(out_val);
	while(ext1.Size>0)
	{
		ext2.Clear();
		for(i=0;i<ext0.Size;i++)
		{
			GetAttr(ext0[i],in_attr,inst0);
			for(j=0;j<ext1.Size;j++)
			{
				if(inst0==ext1[j])
				{
					GetAttr(ext0[i],out_attr,inst0);
					if(inst0==in_val) return false;
					ext2.Add(inst0);
					ext0.Remove(i);
					i--;
					break;
				}
			}
		}
		ext1.Clear();
		ext1.Append(ext2);
	}
	return true;
}
//**********************************************************************
bool CaplStepData::TestUniqueAttrValuesBN(LPCTSTR entity, CaplInstance *tested_inst,
										  int count, CaplAttrValue *values)
{
	return TestUniqueAttrValues(GetEntityBN(entity),tested_inst, count,values);
}

bool CaplStepData::TestUniqueAttrValues(CaplEntity *entity, CaplInstance *tested_inst,
										int count, CaplAttrValue *values, bool bIgnoreSpace)
{
	if(0==entity) return false;
	if(count<1)  return false;
	if(0==values) return false;
	
#ifdef _DEBUG	
	if(bIgnoreSpace)
	{
		aplMessageBox(APL_T("Использование необрабатываемого параметра bIgnoreSpace в CaplStepData::TestUniqueAttrValues !!!"),MB_OK|MB_ICONSTOP);
	}
#else
	if(bIgnoreSpace){} // чтобы компилятор не ругался
#endif
	
	int i,j;
	aplExtent ext;
	CaplValue val;
	GetEntityExtent(entity,ext);
	for(i=0;i<ext.Size;i++)
	{
		bool equ=true;
		if(ext[i]!=tested_inst)
		{
			for(j=0;j<count;j++)
			{
				CaplAttr *attr=values[j].attr;
				if(GetAttr(ext[i],attr,val))
				{
					if(val!=values[j].value) 
					{
						equ=false;
						break;
					};
				}
				else {equ=false;break;}
			}
			if(equ) return false;
		}
	}
	//!!!!!!!
	//!!!!!!!   Вызов ее в базе
	//!!!!!!!
	return true;
}
//**********************************************************************
// Поиск Instance в массивах
void FindAggrBottom(CaplStepData &data, CaplAggr *arr, aplExtent &instances)
{
	int i, Range;

	if ( !( (arr->type == aplAGGR) || (arr->type == aplINSTANCE)) ) return;	// Дальше копать нечего
	
	Range = arr->GetSize();

	if ( arr->type == aplAGGR )
		for (i = 0; i < Range; i++) FindAggrBottom(data, arr->aggr[i]->aggrval, instances);
	else
		for (i = 0; i < Range; i++) data.aplGetAllInstanceRef(arr->aggr[i]->instval, instances);

}
//**********************************************************************
void CaplStepData::aplGetAllInstanceRef(CaplInstance *inst, aplExtent &instances)
{
	if(0==inst) return;
	if(0==inst->GetType()) return;
	if(0==inst->attrs) return;

	int i, li_attr_col;	

	if ( instances.Find(inst) != -1)
	{
		// считаем что если инстанс есть в экстенте - она уже раскручивалась 
		// данной функцией и ее атрибуты уже проработаны
		return;
	}
	instances.Add(inst);				// Текущий instance - в результат

	li_attr_col = inst->GetType()->all_attrs.GetSize();	// Число атрибутов экземпляра

	if (!li_attr_col) return;	// Nema!
	else
	{
			// Ищем ссылки на instance
		for (i = 0; i < li_attr_col; i++)
		{
			if ( inst->attrs[i].value.type == aplINSTANCE ) // значение - instance!
			{
				aplGetAllInstanceRef(inst->attrs[i].value.instval, instances); // Продолжаем рекурсивно
			}
				// Проверяем агрегатные типы вглубь
			else if (inst->attrs[i].value.type == aplAGGR )						
			{
				FindAggrBottom(*this, inst->attrs[i].value.aggrval, instances);
			}
		}
	}	
}
//**********************************************************************
void RemapAggrToNewDB(aplExtent &instances,aplExtent &new_instances, CaplAggr &aggr)
{
	int i;
	int Range = aggr.GetSize();	
	int Reference;

	// Массив вложенный
	CaplValue *val;
	for ( i=0; i < Range; i++ )
	{
		val=aggr.GetByIndex(i);
		if(0==val) continue;
		if ( val->type == aplAGGR ) RemapAggrToNewDB(instances,new_instances,*(val->aggrval));
		else if ( val->type == aplINSTANCE )
		{
			Reference = instances.Find(val->instval);
			if (Reference == -1) val->instval = 0;	// Instance не входит в набор
			else val->instval = new_instances[Reference];	// перекрестная ссылка
		}	
	}
};
//**********************************************************************
void CaplStepData::aplFarCopyInstances(aplExtent &instances, CaplStepData &out_data, bool clear_data)
{
	long i, j, li_items_col, li_attr_col;

	CaplEntity * pEntityRef;			// Рабочие указатели
	CaplAttr * pAttrRef;	

	// Проверка на удаленные
	for(i=0;i<instances.GetSize();i++)
	{
		CaplInstance *inst=instances[i];
		if(0==inst){instances.Remove(i);i--; continue;}
		if(0==inst->GetType()){instances.Remove(i); i--;}
	}
	li_items_col = instances.GetSize();
	if (li_items_col == 0) return;

	aplExtent new_inst;
	new_inst.Unique=false;

	if(clear_data)
		out_data.ClearData();

	// Сначала копируем Instancы
	for (i = 0; i < li_items_col;  i++)		// По всем данным Inst-ам
	{
		pEntityRef = out_data.GetEntityBN( instances[i]->GetType()->name );	// Ссылка на тип Instance во втором StepData		
		new_inst.Add(out_data.CreateInstance( pEntityRef ));	// Добавили экземпляр
	}
			
	for (i = 0; i < li_items_col; i++)
	{
		if(0==new_inst[i]) continue;
		if(0==instances[i]->attrs) continue;
		// Копируем атрибуты и перекрестные ссылки :
		li_attr_col = instances[i]->GetType()->all_attrs.GetSize();				
		for (j = 0; j < li_attr_col; j++)	
		{	
			pAttrRef = out_data.GetAttrDefinition(new_inst[i]->GetType(), instances[i]->GetType()->all_attrs[j]->name );

			// Атрибут INSTANCE
			if ( instances[i]->attrs[j].value.type == aplINSTANCE )
			{
				int Reference = instances.Find(instances[i]->attrs[j].value.instval);
				if ( Reference == -1) out_data.PutAttr(new_inst[i], pAttrRef, (CaplInstance*)0); // ссылка за пределы массива instances
				else out_data.PutAttr(new_inst[i], pAttrRef, new_inst[ Reference ] );		// перекрестная ссылка
			}
			else	
			{
				// Простой атрибут
				out_data.PutAttr(new_inst[i], pAttrRef, instances[i]->attrs[j].value);	// Присвоили значение
					// Атрибут AGGR
				if ( instances[i]->attrs[j].value.type == aplAGGR )	// Если атрибут - AGGR
				{
					RemapAggrToNewDB(instances,new_inst,*(new_inst[i]->attrs[j].value.aggrval));
				}
			}			
		}

	}
}
//**********************************************************************
bool CaplStepData::FindInstanceUsersBN(CaplInstance *inst, LPCTSTR ent_name, LPCTSTR attr_name, aplExtent &extent)
{
	extent.Clear();
	if(0==inst) return false;
	if(aplIsStringEmpty(ent_name)) return false;
	if(aplIsStringEmpty(attr_name)) return false;
	
	CaplEntity *ent=GetEntityBN(ent_name);
	if(0==ent) return false;
	CaplAttr *attr=GetAttrDefinition(ent,attr_name);
	if(0==attr) return false;

	return FindInstanceUsers(inst,ent,attr,extent);
}
//**********************************************************************
bool CaplStepData::FindInstanceUsers(CaplInstance *inst,CaplEntity *ent, CaplAttr* attr, aplExtent &extent)
{
	extent.Clear();
	if(0==inst) return false;
	if(0==ent) return false;
	if(0==attr) return false;

	int i,k;
	aplExtent ext;
	GetEntityExtent(ent,ext);
	for(i=0;i<ext.Size;i++)
	{
		CaplValue *val;
		GetAttr(ext[i],attr,&val);
		if(0==val) continue;
		if(val->type==aplINSTANCE)
		{
			if(val->instval==inst)extent.Add(ext[i]);
		}
		else if(val->type==aplAGGR)
		{
			CaplAggr *aggr=val->aggrval;
			if(aggr!=0)
			{
				CaplValue *value;
				for(k=0;k<aggr->GetSize();k++)
				{
					value=aggr->GetByIndex(k);
	
					if(value!=0)
					{
						if(value->type==aplINSTANCE)
						{
							if(value->instval==inst) extent.Add(ext[i]);
						}
					}
				} 
			}
		}
	}
	return true;
}

int CSortClass::CompareInstId( const void *p1,const void *p2)
{
	CaplInstance *i1,*i2;
	i1=*((CaplInstance**)p1);
	i2=*((CaplInstance**)p2);
	if(i1->GetId()<i2->GetId())return -1;
	if(i1->GetId()>i2->GetId())return 1;
	if(i1<i2)return -1;
	if(i1>i2)return 1;
	return 0;
}

int CSortClass::CompareInstType( const void *p1,const void *p2)
{
	CaplInstance *i1,*i2;
	i1=*((CaplInstance**)p1);
	i2=*((CaplInstance**)p2);
	if(0==i1->GetType() && i2->GetType()!=0)return -1;
	if(i1->GetType()!=0 && 0==i2->GetType())return 1;
	if(0==i1->GetType() && 0==i2->GetType())return 0;
	if(i1->GetType()->id<i2->GetType()->id)return -1;
	if(i1->GetType()->id>i2->GetType()->id)return 1;
	return 0;
}

int CSortClass::CompareInst( const void *p1,const void *p2)
{
	CaplInstance *i1=0,*i2=0;
	i1=*((CaplInstance**)p1);
	i2=*((CaplInstance**)p2);
	if(i1<i2)return -1;
	if(i1>i2)return 1;
	return 0;
}

void CSortClass::SortExtentById(aplExtent &ext)
{
	if(ext.Size<2) return;
	if(ext.Sorted_id) return;
	qsort(ext.Data,ext.Size,sizeof(pCaplInstance),CompareInstId);
	ext.Sorted_id=true;
	ext.Sorted_inst=false;
}	
void CSortClass::SortExtentByType(aplExtent &ext)
{
	if(ext.Size<2) return;
	qsort(ext.Data,ext.Size,sizeof(pCaplInstance),CompareInstType);
	ext.Sorted_id=false;
	ext.Sorted_inst=false;
}	
void CSortClass::SortExtentByInst(aplExtent &ext)
{
	if(ext.Size<2) return;
	if(ext.Sorted_inst) return;
	qsort(ext.Data,ext.Size,sizeof(pCaplInstance),CompareInst);
	ext.Sorted_inst=true;
	ext.Sorted_id=false;
}

void CSortClass::SortArrayById(pCaplInstance **instmap,int instmapsize)
{
	if(0==instmap)return;
	if(instmapsize<2) return;
	qsort(*instmap,instmapsize,sizeof(CaplInstance*),CompareInstId);
}
void CSortClass::SortArrayByInst(pCaplInstance **instmap,int instmapsize)
{
	if(0==instmap)return;
	if(instmapsize<2) return;
	qsort(*instmap,instmapsize,sizeof(pCaplInstance),CompareInst);
}

void CSortClass::MakeExtUnique(aplExtent &ext,bool remove_zero)
{
	ext.Size = std::unique(ext.Data, ext.Data + ext.Size) - ext.Data;
	if(!remove_zero) return;
	for (int j=0; j<ext.Size; ++j)
	{
		if(ext[j] == 0) {ext.Remove(j); break;}
	}
/*
	if(ext.Size<2 )return;
	bool old_flag=ext.Unique;ext.Unique=false;
	int i=0;
	for(i=ext.Size-1;i>0;i--){
		if(ext.GetAt(i)==ext.GetAt(i-1)){
			ext.Remove(i);
		}
		if(remove_zero && ext.GetAt(i)==0){
			ext.Remove(i);
		}
	}
	ext.Unique=old_flag;
*/
}


void GetNameType(aplValueType type, CString &name)
{
	switch(type){
	case aplNOTYPE	:name=_T("aplNOTYPE");break;
	case aplINSTANCE:name=_T("aplINSTANCE");break;
	case aplAGGR	:name=_T("aplAGGR");break;
	case aplINTEGER	:name=_T("aplINTEGER");break;
	case aplREAL	:name=_T("aplREAL");break;
	case aplSTRING	:name=_T("aplSTRING");break;
	case aplBOOL	:name=_T("aplBOOL");break;
	case aplLOGICAL	:name=_T("aplLOGICAL");break;
	case aplBINARY	:name=_T("aplBINARY");break;
	case aplSELECT	:name=_T("aplSELECT");break;
	case aplBLOB	:name=_T("aplBLOB");break;
	case aplENUMERATION		:name=_T("aplENUMERATION");break;
	case aplINTEGRATEDBIN	:name=_T("aplINTEGRATEDBIN");break;
		
	default:name= APL_T("неизвестен");break;
	}
}

CString GetNameType(aplValueType type)
{
	CString name;
	GetNameType(type, name);
	return name;
}

CString GetNameAccess(aplAccessModeType access, bool is_rus,bool print_not_def, bool case_first)
{
	CString name;
	switch(access){
	case aplOWN:if(is_rus){if(case_first){name= APL_T("Полный доступ");}else{name= APL_T("полный доступ");}}else{name=_T("OWN");};break;
	case aplRW:if(is_rus){if(case_first){name= APL_T("Чтение-запись");}else{name= APL_T("чтение-запись");}}else{name=_T("RW");};break;
	case aplRO:if(is_rus){if(case_first){name= APL_T("Только чтение");}else{name= APL_T("только чтение");}}else{name=_T("RO");};break;
	case aplNO:if(is_rus){if(case_first){name= APL_T("Нет доступа");}else{name= APL_T("нет доступа");}}else{name=_T("NA");};break;
	case aplNOT_DEFINED:
		if(print_not_def)
		{
			if(is_rus){if(case_first){name= APL_T("Не определен");}else{name= APL_T("не определен");}}
			else{name=_T("NOT_DEFINED");};
		}
		break;
	default:name=_T("");
	}
	return name;
}

bool CaplStepData::ClearInstances(aplExtent &ext)
{
	SetLastError(0);
	int i;
	int ind;
	for(i=0; i<ext.Size; i++)
	{
		ind = entities.Find(ext[i]->GetType());
		instances.Remove(instances.Find(ext[i]));
		if(ind>-1)
			entities[ind]->instances.Remove(entities[ind]->instances.Find(ext[i]));
		else
			SetLastError(APLAPIERR_BADTYPE);
	}

	return true;
}

bool CaplStepData::ClearInstances(CaplEntity* pEntyti)
{
	if(pEntyti==NULL) return false;

	aplExtent ext;
	int i, j;
	int ind;
	CaplTAggr<CaplEntity*, CaplEntity*, APLAGGR_LIST> subtypes;
	CaplTAggr<CaplEntity*, CaplEntity*, APLAGGR_LIST> tmp;
	CaplTAggr<CaplEntity*, CaplEntity*, APLAGGR_LIST> last_level;
	subtypes.Add(pEntyti);
	last_level.Add(pEntyti);
	do
	{
		tmp.Clear();
		for(i=0; i<last_level.Size; i++)
		{
			for(j=0; j<last_level[i]->subtypes.Size; j++)
				tmp.Add(last_level[i]->subtypes[j]);
		}
		last_level.Clear();
		last_level.Append(tmp);
		subtypes.Append(tmp);
	}while(tmp.GetSize()>0);
	GetEntityExtent(pEntyti, ext);
	for(i=0; i<subtypes.GetSize(); i++)
	{
		ind = entities.Find(subtypes[i]);
		if(ind>-1)
			entities[ind]->instances.Clear();
		else
			SetLastError(APLAPIERR_BADTYPE);
	}
	for(i=0; i<ext.Size; i++)
		instances.Remove(instances.Find(ext[i]));
	
	return true;
}

aplAccessModeType CaplInstance::GetAccessmode(bool check_admin_mode) const
{
	if (!m_data)
	{
		return aplNOT_DEFINED ;
	}
	if(check_admin_mode)
	{
		if(m_data->GetAdminMode())return aplOWN;
	}
	return m_accessmode;
}

bool CaplStepData::ParseAccessStringPattern(const CString &str_pattern, aplUsers &access_table, CString *err_mess, bool ignore_deleted_users, aplExtent *external_users)
{
	access_table.Clear();
	if(err_mess!=0)*err_mess=_T("");
	aplExtent ext_u;
	aplAccessList *list,*list_next;
	aplAccessList *list_def=0;
	bool is_individual=false;
	aplAccessEl *el,*el_next;
	CaplInstance* user_internal=0;
	aplAccessModeType access_internal;
	CString buf;
	CString err_mess_internal=_T("");
	int i,pos_cur=0,pos_old=0;
	bool ignore_rec=false;
	bool flag_pattern;
	CaplInstance *pattern;
	int pattern_id;
	bool non_auto_pattern=false;

	GetEntityExtentBN(_T("apl_access_agent"),ext_u);
	if(external_users != 0)
		ext_u.Append(*external_users);

	if(str_pattern.IsEmpty())
	{
		for(i=0;i<ext_u.GetSize();i++){
			list=new aplAccessList();
			list->user=ext_u.GetAt(i);
			access_table.Add(list);
			el=new aplAccessEl();list->Add(el);
		}
		list=new aplAccessList();
		list->user=0;
		el=new aplAccessEl();list->Add(el);
		access_table.Add(list);
	}else{
		CSortClass::SortExtentById(ext_u);
		SetLastError(0);
		while(pos_old<(int)str_pattern.GetLength())
		{
			ignore_rec=false;
			flag_pattern=false;
			pattern=0;pattern_id=0;
			access_internal=aplNOT_DEFINED;
			// идентификатор instance
			pos_cur=str_pattern.Find(_T(':'),pos_old);
			if(pos_cur==-1)break;
			buf=str_pattern.Mid(pos_old,pos_cur-pos_old);
			pos_old=pos_cur+1;
			i=_atoi(buf);
			if(0==i)
			{
				if(buf==_T("0")){
					user_internal=0;
				}else if(buf==_T("pat")){
					flag_pattern=true;
				}else{
					err_mess_internal+= APL_T("\r\nНекорректный идентификатор пользователя '") + buf + _T("'");
					ignore_rec=true;
					if(buf.Find(APL_T("<шаблон"))!=-1)non_auto_pattern = true;

				}
			}
			else if(i>5 && i<3000)
			{
				ignore_rec=true;
			}
			else
			{
				aplQFindInstIdInExtent(ext_u,i,-1,true,&user_internal);
				if(0==user_internal)
				{
					// Всегда игнорируем ошибки для служебных временных инстансов, а для остальных - по флагу
					if(!ignore_deleted_users && i > 3000)
					{
						buf.Format( APL_T("\r\nНе найден пользователь с идентификатором instance %i. Возможно, пользователь был удален"),i);
						err_mess_internal+=buf;
					}
					ignore_rec=true;
				}
				is_individual=true;
			}
			// тип доступа
			pos_cur=str_pattern.Find(_T(';'),pos_old);
			if(pos_cur==-1){
				// если последней точки с запятой нет - читаем до конца строки
				if(((int)str_pattern.GetLength())>pos_old){
					pos_cur=str_pattern.GetLength();
				}else{
					break;
				}
			}
			buf=str_pattern.Mid(pos_old,pos_cur-pos_old);
			pos_old=pos_cur+1;
			if(ignore_rec){
				continue;
			}
			if(flag_pattern)
			{
				if(buf[0]!=_T('#'))
				{
					/*Пишите доступ правильно, мать вашу! Игнорируем команду*/
					err_mess_internal+= APL_T("\r\nНеверно задан шаблон доступа (идентификатор должен начинаться с '#'): '") + buf + _T("'");
					continue;
				}
				buf.Delete(0);
				pattern_id = _atoi(buf);
				if(0==pattern_id)
				{
					if(buf!=_T("0"))
					{
						/*Пишите доступ правильно, мать вашу! Игнорируем команду*/
						err_mess_internal+= APL_T("\r\nНеверно задан шаблон доступа: '") + buf + _T("'");
						continue;
					}
				}
				else
				{
					pattern = GetInstById(pattern_id);
					if(0==pattern)
					{
						buf.Format( APL_T("\r\nНе найден шаблон доступа с идентификатором instance %i. Возможно, шаблон был удален"),pattern_id);
						err_mess_internal+=buf;
						continue;
					}
				}
			}
			else if(buf==_T("OWN")){access_internal=aplOWN;}
			else if(buf==_T("RW")){access_internal=aplRW;}
			else if(buf==_T("RO")){access_internal=aplRO;}
			else if(buf==_T("NA")){access_internal=aplNO;}
			else{
				/*Пишите доступ правильно, мать вашу! Игнорируем команду*/
				err_mess_internal+= APL_T("\r\nНеверно задан уровень доступа: '") + buf + _T("'");
				continue;
			}

			if(list_def != 0 && pattern !=0)
			{
				el = list_def->GetAt(0);
				if(el!=0)
					el->pattern=pattern;
			}
			else
			{
				list=new aplAccessList();
				list->user=user_internal;
				if(0==user_internal){list_def=list;}

				el=new aplAccessEl();
				el->access=access_internal;
				
				list->Add(el);
				access_table.Add(list);
			}
		}
		if(is_individual && list_def!=0){
			for(i=0;i<list_def->GetSize();i++){
				list_def->GetAt(i)->is_individual=true;
			}
		}
		if(0==access_table.GetSize()){
			err_mess_internal+= APL_T("\r\nВ строке нет корректных параметров доступа");
		}
		if(err_mess_internal != _T(""))
		{
			buf = APL_T("При разборе шаблона доступа возникли следующие ошибки:") + err_mess_internal;
			if(non_auto_pattern)
			{
				buf += APL_T("\r\n\r\n !! Вы используете шаблон доступа, предназначенный для \r\n человека, а не для машинной обработки!!!");
			}
			if(err_mess!=0)
			{
				*err_mess=err_mess_internal;
			}
			else
			{
				SetErrorAnnotation(buf);
				SetLastErrorEx(APLAPIERR_BADDATA, false, _T(__FILE__), __LINE__);
			}
			return false;
		}
		// попытаемся упорядочить строчку - выкинуть дубли
		for(i=0;i<access_table.GetSize()-1;i++)
		{
			list=access_table[i];
			list_next=access_table[i+1];
			if(list->user==list_next->user)
			{
				el=list->GetAt(0);
				el_next=list_next->GetAt(0);
				if(el->access==el_next->access)
				{
					// дубль, выкидываем
					// но сначала проверяем, нет ли в каком шаблона
					if(el->pattern==0 && el_next->pattern!=0)el->pattern = el_next->pattern;
					access_table.RemoveAtWithDel(i+1);
					i--;
					continue;
				}
			}
		}

	}
	return true;
}

bool CaplStepData::PrintAccessStringPattern(aplUsers &access_table, CString &str_pattern, bool is_rus, bool *p_is_temp_users, bool def_access)
{
	aplExtent ext_u;
	aplAccessList *list;
	aplAccessEl *el;
	CString buf,buf1;
	int i;
	str_pattern = _T("");
	if(p_is_temp_users != 0)
		*p_is_temp_users = false;
// 	if(m_load_all_users){
// 		LoadAllUsers(ext_u);
// 	}else{
		GetEntityExtentBN(_T("apl_access_agent"),ext_u);
// 	}
	access_table.Sort();
	for(i=0;i<access_table.GetSize();i++)
	{
		list=access_table.GetAt(i);
		if(0==list)continue;
		if(p_is_temp_users !=0 && list->user!=0)
		{
			if(list->user->GetId()<3000) *p_is_temp_users = true;
		}
		if(!def_access && 0==list->user) continue;
		buf=UserNameDecode(list->user,is_rus) + _T(":");
		if(0==list->GetSize())
		{
			continue;
		}
		else
		{
			el=list->GetAt(0);
			if(0==el)continue;
			if(el->access==aplNOT_DEFINED)continue;
			buf+=GetNameAccess((aplAccessModeType)el->access,is_rus);
			if(list->user==0 && el->pattern !=0)
			{
				buf1.Format(_T("#%i"), el->pattern->GetId());
				buf += _T(";");
				if(is_rus)
				{
					buf += APL_T("<шаблон доступа>:") + buf1;
				}
				else
				{
					buf += APL_T("pat:") + buf1;
				}
			}
		}
		str_pattern += buf + _T(";");
		if(is_rus)str_pattern += _T(" ");
	}
	str_pattern.TrimRight(_T(';'));

	return true;
}

CString CaplStepData::UserNameDecode(CaplInstance* user,bool for_user, bool case_first)
{
	CString buf;
	if(for_user){
		if(0==user){
			if(case_first){buf= APL_T("По умолчанию");}else{buf= APL_T("по умолчанию");}
		}else if(user->GetId()==1){
			if(case_first){buf= APL_T("Текущий пользователь");}else{buf= APL_T("текущий пользователь");}
		}else if(user->GetId()==2){
			if(case_first){buf= APL_T("Группа текущего пользователя");}else{buf= APL_T("группа текущего пользователя");}
		}else{
			CString buf1;
			GetAttrBN(user,_T("name"),buf1);
			buf = _T("'") + buf1 +  _T("'");
		}
	}else{
		buf.Format(_T("%i"),(0==user)?0:user->GetId());
	}
	return buf;
}


/// Преобразует дату в строку
bool aplDate2String(COleDateTime &time,CString &buf)
{
	buf = _T("");
	if(time.GetStatus()==COleDateTime::invalid) return false;
	if(time.GetStatus()==COleDateTime::null) return true;

	buf=time.Format(_T("%Y%m%d%H%M%S"));
	return true;
}

// Функция преобразовывает макросы __DATE__ и __TIME__ в стандартный формат
COleDateTime aplBuildDateTime2Odt(const char* date, const char* time) 
{
	std::string strDate(date);
	std::string strTime(time);

	int year=0, month=0, day=0, hour=0, minute=0, sec=0;
	std::istringstream issD(strDate);
	std::string monthStr;
	issD >> monthStr >> day >> year;
	if(monthStr=="Jan")month=1; 
	else if(monthStr=="Feb")month=2; 
	else if(monthStr=="Mar")month=3; 
	else if(monthStr=="Apr")month=4;
	else if(monthStr=="May")month=5; 
	else if(monthStr=="Jun")month=6; 
	else if(monthStr=="Jul")month=7; 
	else if(monthStr=="Aug")month=8;
	else if(monthStr=="Sep")month=9; 
	else if(monthStr=="Oct")month=10; 
	else if(monthStr=="Nov")month=11; 
	else if(monthStr=="Dec")month=12;

	std::istringstream issT(strTime);
	char colon;
	issT >> hour >> colon >> minute >> colon >> sec;

	return COleDateTime(year, month, day, hour, minute, sec);
}

#ifdef _MFC_VER

bool aplDate2String(time_t &rawtime ,CString &buf) //-V669
{
	buf = _T("");
	if(rawtime == 0) return true;
	COleDateTime dt(rawtime); 
	if(dt.GetStatus()== COleDateTime::valid)
	{
		aplDate2String(dt, buf);
		return true;
	}
	return false;
}
#endif


/// Преобразует строку в дату
bool aplString2Date(CString &buf, COleDateTime &time)
{
	time.SetStatus(COleDateTime::null);
	if(buf.GetLength()<14) return false;
	int nYear,  nMonth,  nDay,  nHour,  nMin,  nSec;

	nYear=_atoi(LPCTSTR(buf.Left(4)));
	nMonth=_atoi(LPCTSTR(buf.Mid(4,2)));
	nDay=_atoi(LPCTSTR(buf.Mid(6,2)));
	nHour=_atoi(LPCTSTR(buf.Mid(8,2)));
	nMin=_atoi(LPCTSTR(buf.Mid(10,2)));
	nSec=_atoi(LPCTSTR(buf.Mid(12,2)));
	if(nSec>59) nSec=59;
	if(nMin>59) nMin=59;
	if(nHour>23) nHour=23;

	if(nMonth>12) nMonth =12; 
	else if(nMonth<1) nMonth =1; 

	if(nDay>=29)
	{
		if(nMonth==2) {if(nDay>29) nDay=29;}
		else if(nMonth==1 || nMonth==3 || nMonth==5 || nMonth==7 || nMonth==8 || nMonth==10 || nMonth==12) {if(nDay>31) nDay=31;}
		else if(nDay>30) nDay=30;
	}
	return 0==time.SetDateTime(  nYear,  nMonth,  nDay,  nHour,  nMin,  nSec );
}

#ifdef _MFC_VER

bool aplString2Date2String(CString &buf)
{
	if(buf.IsEmpty())
		return false;

	COleDateTime time;
	aplString2Date(buf, time);

	if(time.GetStatus() == COleDateTime::valid)
	{
		buf.Format(_T("%.2d.%.2d.%.4d"), time.GetDay(), time.GetMonth(), time.GetYear());
		return true;
	}

	return false;
}

/** Преобразует строку в дату и потом в строку вида ДД.ММ.ГГГГ ЧЧ:ММ:СС*/
bool aplString2DateTime2String(CString &buf)
{
	if(buf.IsEmpty())
		return false;

	COleDateTime time;
	aplString2Date(buf, time);

	if(time.GetStatus() == COleDateTime::valid)
	{
		buf.Format( _T("%.2d.%.2d.%.4d %2d:%2d:%2d"), time.GetDay(), time.GetMonth(), time.GetYear(), time.GetHour(), time.GetMinute(), time.GetSecond() );
		return true;
	}

	return false;
}

bool aplString2Date(CString &buf, time_t &time)
{
	time= time_t(0);

	//ПОСЛЕ ПЕРЕХОДА LSS3 на 64-битные даты - ВЫПИЛИТЬ НАХЕР!!!!!!
	if(buf.Left(8) == _T("19700101") || buf.Left(8) == _T("19691231"))
	{
		return false;
	}

	if(buf.IsEmpty()==true)
	{
		return false;
	}
	COleDateTime dt;
	aplString2Date(buf, dt);
	if(dt.GetStatus()==COleDateTime::valid)
	{
		bool bValid(true);
		// максимально возможная дата в CTime
		COleDateTime dtMaxDate(3000, 12, 31, 23, 59, 59);
		if (dt > dtMaxDate)
		{
			dt = dtMaxDate;
			bValid = false;
		}

		SYSTEMTIME sysTime;
		dt.GetAsSystemTime(sysTime);
		CTime cTime(sysTime);
		time= cTime.GetTime();

		return bValid;
	}
	return false;
}

bool AFX_EXT_API aplString2Date64(CString &buf, INT64 &i64Time)
{
	i64Time= 0;

	if(buf.IsEmpty()==true)
	{
		return false;
	}
	COleDateTime dt;
	aplString2Date(buf, dt);
	if(dt.GetStatus()==COleDateTime::valid)
	{
		SYSTEMTIME sysTime;
		dt.GetAsSystemTime(sysTime);
		SystemTimeToFileTime(&sysTime, (FILETIME*)&i64Time) ;
		return true;
	}
	return false;
}

bool AFX_EXT_API aplDate2String64(INT64 &i64Time, CString &buf, bool NullifyTime /*= false*/)
{
	buf = _T("");
	if (i64Time==0)
		return true;	// сбрасываем дату
	SYSTEMTIME sysTime ;
	FileTimeToSystemTime((FILETIME*)(&i64Time), &sysTime) ;
	COleDateTime time (sysTime) ;
	if (NullifyTime)
	{
		time.SetDateTime(time.GetYear(),time.GetMonth(),time.GetDay(), 0,0,0);
	}
	if(time.GetStatus()==COleDateTime::invalid) return false;
	if(time.GetStatus()==COleDateTime::null) return true;

	buf=time.Format(_T("%Y%m%d%H%M%S"));
	return true;
}

bool aplDate2ISO8601(const CString& sDate, CString& sISO)
{
	sISO.Empty();
	if (sDate.IsEmpty() || sDate.GetLength()<8)
		return true;

	CString sDatePart = sDate.Left(8);
	CString sTimePart = sDate.Right(sDate.GetLength()-8);

	sDatePart.Insert(6, _T('-'));
	sDatePart.Insert(4, _T('-'));

	int iSep = sTimePart.GetLength()-2;
	while(iSep>0)
	{
		sTimePart.Insert(iSep, _T(':'));
		iSep -= 2;
	}

	if (sTimePart.GetLength()>0)
		sISO.Format(_T("%sT%s"), LPCTSTR(sDatePart), LPCTSTR(sTimePart));
	else
		sISO = sDatePart;

	return true;
}

bool aplDateISO8601ToString(const CString& sISO, CString& res, bool bFormatLSS3 /*= false*/)
{
	res.Empty();
	if(sISO.IsEmpty()) return true;

	COleDateTime time;
	time.SetStatus(COleDateTime::null);

	int iT = sISO.Find(_T('T'));
	if (iT<0)
		iT = sISO.Find(_T('t'));

	CString sDate, sTime;
	if (iT<0)
	{
		if (sISO.Find(_T('.'))<0)
		{
			// вся строка является датой в ISO 8601
			sDate = sISO;
			sTime = _T("");
		}
		else
			return false;
	}
	else
	{
		sDate = sISO.Left(iT);
		sTime = sISO.Right(sISO.GetLength()-iT-1);
	}

	sDate.Trim();
	sTime.Trim();

	bool bPlus(true);
	int iDeltaMin(0);
	iT = sTime.Find(_T('+'));
	if (iT<0)
	{
		iT = sTime.Find(_T('-'));
		bPlus = false;
	}
	if (iT>-1)
	{
		CString sTimePart = sTime.Left(iT);
		CString sUTCPart = sTime.Right(sTime.GetLength()-iT-1);
		sTime = sTimePart;

		int nUTCHour(0), nUTCMin(0);
		sUTCPart.Trim();
		sUTCPart.Remove(_T(':'));
		if (!sUTCPart.IsEmpty())
		{
			if (sUTCPart.GetLength()==4)
			{
				nUTCHour=_atoi(LPCTSTR(sUTCPart.Left(2)));
				nUTCMin=_atoi(LPCTSTR(sUTCPart.Mid(2,2)));
			}
			else
				nUTCHour=_atoi(sUTCPart);

			nUTCMin = nUTCMin<60?nUTCMin:0;
			nUTCHour = nUTCHour<24? nUTCHour:0;
			nUTCMin += 60*nUTCHour;
		}

		if (nUTCMin!=0)
		{
			// надо привести из UTC к локальному времени
			TIME_ZONE_INFORMATION TimeZoneInfo;
			GetTimeZoneInformation(&TimeZoneInfo);
			iDeltaMin = -(TimeZoneInfo.Bias+(bPlus?1:-1)*nUTCMin);
		}
	}

	// убираем знаки -
	sDate.Remove(_T('-'));
	// убираем знаки :
	sTime.Remove(_T(':'));

	if (sDate.GetLength()!=8)
		return false;

	int nYear,  nMonth,  nDay,  nHour(0),  nMin(0),  nSec(0);

	nYear = _atoi(LPCTSTR(sDate.Left(4)));
	if (nYear<1900)
		return false;
	nMonth = _atoi(LPCTSTR(sDate.Mid(4,2)));
	if (nMonth<1 || nMonth>12)
		return false;
	nDay = _atoi(LPCTSTR(sDate.Mid(6,2)));
	if (nDay<1 || nDay>31)
		return false;
	if (!sTime.IsEmpty())
	{
		if (sTime.GetLength()>=4)
		{
			nHour=_atoi(LPCTSTR(sTime.Left(2)));
			nMin=_atoi(LPCTSTR(sTime.Mid(2,2)));
		}
		if (sTime.GetLength()>=6)
			nSec=_atoi(LPCTSTR(sTime.Mid(4,2)));
		nSec = nSec<60?nSec:0;
		nMin = nMin<60?nMin:0;
		nHour = nHour<24? nHour:0;
	}
	time.SetDateTime(nYear,  nMonth,  nDay,  nHour,  nMin,  nSec);

	if (iDeltaMin!=0)
	{
		COleDateTimeSpan dt_span(0, iDeltaMin/60, iDeltaMin%60, 0);
		time += dt_span;
	}

	if (bFormatLSS3)
		aplDate2String(time, res);
	else
		res.Format(_T("%s %s"), LPCTSTR(time.Format(VAR_DATEVALUEONLY)), LPCTSTR(time.Format(_T("%H:%M:%S"))));

	return true;
}

#endif


CaplInstance* CaplStepData::GetInstById(apl_id id)
{
	CaplInstance* inst=0;
	// поиск в отсортированном списке инстансов
	aplQFindInstIdInExtent(*((aplExtent*)(&instances)),id,-1,true,&inst);
	return inst;
}

#ifdef _MFC_VER

void aplRecursiveGetDirs(LPCTSTR startdir, LPCTSTR mask, CStringArray &sArrayMasks)
{
	CFileFind ff;
	CString path,buf;

	path=startdir; path+=_T("*.*");

	if(!ff.FindFile(path)) return;
	BOOL bNextFile=TRUE;
	while(bNextFile)
	{
		bNextFile=ff.FindNextFile();
		if(ff.IsDots()) continue;
		if(!ff.IsDirectory( )) continue;
		path=ff.GetFilePath();
		path+=_T("\\");

		buf=path; buf+=mask; 
		sArrayMasks.Add(buf);
		aplRecursiveGetDirs(path,mask,sArrayMasks);
	}
}


// Получение списка проверяемых файлов
bool aplReadFiles4AutoUpdate(LPCTSTR _folder_files, LPCTSTR _folder_luf, CStringArray &list_upd_files, bool bInteractive, CString &err_mes)
{
	CString folder_files = _folder_files;
	CString folder_luf = _folder_luf;
	bool find_all = folder_files!=folder_luf;
	CString buf = folder_luf + _T("*.luf");
	CFileFind ff;
	int i;
	err_mes = _T("");
	list_upd_files.RemoveAll();

	if(!ff.FindFile(buf)) 
	{
		err_mes = APL_T("Автообновление:\n\n   Файлы со списком обновляемых файлов (*.luf) не найдены !\n\n   Обновление невозможно !!!");
		if (bInteractive)
			AfxMessageBox(err_mes);
		return false;
	}
	err_mes = APL_T("Файл luf найден ") +buf;

	CaplStrMap map_update_files;

	BOOL bNextFile=TRUE;
	while(bNextFile)
	{
		bNextFile=ff.FindNextFile();
		buf=folder_luf + ff.GetFileName();
		CStdioFile file;
		if(!file.Open(buf,CFile::modeRead|CFile::shareDenyWrite|CFile::typeText))
		{
			err_mes=APL_T("Автообновление:\n\n   Ошибка открытия файла \"");
			err_mes+=buf; err_mes+=_T("\"");

			if (bInteractive)
				AfxMessageBox(err_mes);
			continue;
		}
		while(file.ReadString(buf))
		{
			buf.TrimLeft();buf.TrimRight(); buf.MakeLower();
			if((buf.Find(_T('*'))>=0) || (buf.Find(_T('?'))>=0) || find_all)
			{
				CFileFind ff1;
				CString m_path=folder_files+buf;
				CString fname;
				int i_folder_files_len=folder_files.GetLength();

				CStringArray sArrayMasks;
				int iPosRecusrDir=buf.Find(_T("*\\"));
				if(iPosRecusrDir<0)sArrayMasks.Add(m_path); // Нет ркукрсивных поддирректорий
				else
				{
					//Есть рекурсивные поддирректории
					CString startdir=folder_files,mask;
					if(iPosRecusrDir>0)
					{
						while(iPosRecusrDir>0 && buf[iPosRecusrDir] != _T('\\')) iPosRecusrDir--;
						startdir+=buf.Left(iPosRecusrDir+1);
					}
					int i=buf.ReverseFind(_T('\\'));
					if(i>0) mask=buf.Right(buf.GetLength()-(i+1));
					else mask = _T("*.*");
					
					buf=startdir+mask;
					if(iPosRecusrDir>0) sArrayMasks.Add(buf);

					aplRecursiveGetDirs(startdir,mask,sArrayMasks);
				}
				int iRecSubDir;
				for(iRecSubDir=0; iRecSubDir<sArrayMasks.GetSize(); iRecSubDir++)
				{
					if(!ff1.FindFile(sArrayMasks[iRecSubDir])) continue;
					BOOL bNextFile1=TRUE;
					while(bNextFile1)
					{
						bNextFile1=ff1.FindNextFile();
						if(ff1.IsDots()) continue;
						if(ff1.IsDirectory( )) continue;
						buf=ff1.GetFilePath();
						fname=buf.Right(buf.GetLength()-i_folder_files_len);

						map_update_files.Add(fname,(INT_PTR)0);
					}
				}
			}
			else
			{
				map_update_files.Add(buf,(INT_PTR)0);
			}
		}
		file.Close();
	}

	for(i=0;i<map_update_files.GetSize();i++) list_upd_files.Add(map_update_files.GetAt(i)->str);
	return true;
}

#endif

bool CaplStepData::SetGlobalProcessWmPaint(bool is_process)
{
	bool prev_val = global_process_wm_paint;
	global_process_wm_paint=is_process;
	return prev_val;
}


void aplTranslateDictErrorMessages(CString &description, bool remove_line_break)
{
#ifndef _MFC_VER
	// Для зарубежных локалей незачем переводить англоязычные сообщения
	if(CString("ru_RU")!=CaplTranslate::GetGurLocale()) return;
#endif
	if(remove_line_break) description.Replace(_T("\r\n"), _T("\n"));
	description.Replace(_T("\n Additional dictionary file"), _T("Дополнительный словарь"));
	description.Replace(_T("\n Dictionary file"), _T("Словарь"));
	if(remove_line_break) description.Replace(_T("\n Schema loading error! Error in line"), _T("Ошибка в файле словаря в строке:"));
	description.Replace(_T(" Schema loading error! Error in line"), _T("Ошибка в файле словаря в строке:"));
	description.Replace(_T(" Error description: "), _T(""));
	description.Replace(_T("The entity '"), _T("Энтити '"));
	description.Replace(_T("has several attributes '"), _T(" имеет несколько атрибутов '"));
	description.Replace(_T("with different identifiers"), _T("с конфликтующими идентификаторами"));
	description.Replace(_T("Error reading version"), _T("Ошибка чтения строки с версией словаря"));
	description.Replace(_T("Missed format version's string"), _T("Некорректный формат строки с версией словаря"));
	description.Replace(_T("Missed format of string"), _T("Некорректный формат строки словаря"));
	description.Replace(_T("Error reading entity id"), _T("Не удалось прочитать идентификатор entity"));
	description.Replace(_T("Error reading entity name"), _T("Не удалось прочитать имя entity"));
	description.Replace(_T("Error reading entity type"), _T("Не удалось прочитать тип entity"));
	description.Replace(_T("Missed entity id"), _T("Некорректный идентификатор entity"));
	description.Replace(_T("Missed entity name"), _T("Некорректное имя entity"));
	description.Replace(_T("Redefinition entity id:"), _T("Повторное использование идентификатора entity: "));
	description.Replace(_T("Redefinition entity name "), _T("Повторное использование имени entity "));

	description.Replace(_T("Error reading attr id"), _T("Ошибка чтения строки с идентификатором атрибута"));
	description.Replace(_T("Missed attr id"), _T("Некорректный идентификатор атрибута"));
	description.Replace(_T("Error reading attr's vid"), _T("Ошибка чтения строки с параметром vid атрибута"));
	description.Replace(_T("Error reading attr's entity id"), _T("Ошибка чтения строки с идентификатором entity"));
	description.Replace(_T("Missed attr's entity id"), _T("Некорректный идентификатор entity"));
	description.Replace(_T("Error reading attr's opt"), _T("Ошибка чтения строки с параметром opt атрибута"));
	description.Replace(_T("Error reading attr's redeclarar"), _T("Ошибка чтения строки с параметрами перегрузки"));
	description.Replace(_T("Error reading attr's name"), _T("Ошибка чтения строки с именем атрибута"));
	description.Replace(_T("Missed attr's name"), _T("Некорректное имя атрибута"));
	description.Replace(_T("Error reading attr's type"), _T("Ошибка чтения строки с типом атрибута"));
	description.Replace(_T("Missed attr's type"), _T("Некорректный тип атрибута"));
	description.Replace(_T("Redefinition attr id"), _T("Повторное использование идентификатора атрибута"));

	description.Replace(_T("Error postprocessing id 2 pointer"), _T("Ошибка при проверке соответствия идентификаторов и объектов"));
	description.Replace(_T("Error postprocessing id 2 pointer in supertypes"), _T("Ошибка при проверке соответствия идентификаторов и объектов супертипов"));
}


/** Сохраняет все данные в файле формата json  */
bool CaplStepData::SaveToJsonFile(LPCTSTR file_name, bool save_temporary)
{
	if(aplIsStringEmpty(file_name)) {SetLastErrorWithFileInfo(APLAPIERR_BADFILENAME); return false;}
	SetLastError(0);
	int i,instmapsize;
	CString buf,buf1;


	CaplStringFile file;
	if(!file.Open(file_name,CFile::modeCreate|CFile::modeWrite)){
		SetLastErrorWithFileInfoExtText(APLAPIERR_FILE_IO,file_name); return false;}

#ifdef _UNICODE
	file.SetFileEncoding(aplUTF8);
#endif

	SJsonEl JsonRoot;
	JsonRoot.m_bNoSaveEmptyAttr=true;

	// заполняем заголовок
	JsonRoot.AddHeader(this);

	pCaplInstance *instmap=0;
	// Внимание! instances всегда должен быть отсортирован по id!
	//
	//CSortClass::SortInstByInst(instances);//qSortExtent(*(aplExtent*)&instances); 
	instmapsize=instances.Size;
	instmap=new pCaplInstance[instmapsize];
	for(i=0;i<instmapsize;i++) instmap[i]=instances[i];
	CSortClass::SortArrayByInst(&instmap,instmapsize);

	bool bSaveAsTree=false; //Флаг записи в виде дерева

	aplExtent extRoot;
	aplExtent *ext4Write=&instances;

	if(bSaveAsTree)
	{
		// Формирование списка корневых объектов
		extRoot.Unique=false;
		for(i=0;i<instances.Size;i++) extRoot.Add(instances[i]);
		extRoot.Unique=true;
		RemoveNonRootInstances(extRoot);

		ext4Write=&extRoot;
	}

	for(i=0;i<ext4Write->GetSize();i++)
	{
		CaplInstance *inst_i=ext4Write->GetAt(i); 
		if(inst_i==NULL) continue;
		if(inst_i->GetType()==0) continue;
		if(inst_i->GetTemporary() && !save_temporary) continue;//16.01.2003

		if(bSaveAsTree)
			JsonRoot.AddInst2JsonRoot(inst_i, SJsonEl::flag_QD_all_attrs_in_tree);
		else
			JsonRoot.AddInst2JsonRoot(inst_i, SJsonEl::flag_QD_single_level_attrs, aplNOT_DEFINED, instmap, instmapsize);

	}

	if(instmap!=0) delete[] instmap;

	CString csJson;
	JsonRoot.Print(csJson);

	file.WriteString(csJson);
	file.WriteString(_T("\n"));
	file.Flush();
	file.Close();
	return true;

}

/** Читает данные из файла формата json. */
bool CaplStepData::LoadFromJsonFile(LPCTSTR file_name)
{
	QT_THREADS_DENY_FROM_READ
	if(aplIsStringEmpty(file_name)) {SetLastErrorWithFileInfo(APLAPIERR_BADFILENAME); return false;}

	ClearData();
	CString json_buf = _T(""), err_str;
	SJsonEl input_root;
	CaplMap map_index2inst;

	CaplStringFile file;
	if(!file.Open(file_name,CFile::modeRead)) 
	{SetLastErrorWithFileInfoExtText(APLAPIERR_FILE_IO, file_name); return false;}

	if(!file.ReadAllDataToString(json_buf))
	{
		SetLastErrorWithFileInfoExtText(APLAPIERR_FILE_IO, file_name); 
		file.Close();
		return false;
	}
	file.Close();

	if(!input_root.ParseText2Json(json_buf, err_str))
	{SetLastErrorWithFileInfoExtText(APLAPIERR_INVALIDDATAFORMAT, err_str); return false;}

	if(!input_root.LoadInstancesFromJson(this, map_index2inst, err_str))
	{
		ClearData(); // ClearData сбрасывает ошибку, поэтому ее надо вызывать до SetLastErrorEx
		SetLastErrorWithFileInfoExtText(APLAPIERR_INVALIDDATAFORMAT, err_str); 
		return false;
	}

	//Сортируем инстансы по id
	CSortClass::SortInstById(instances);

	return true;
}


// Cобирает в stdExtent список тех на котоые ссылается inst
void GetReferenceReqursive(CaplInstance *inst, std::set<CaplInstance*> &stdExtent, bool bRevursive)
{
	if(0==inst) return;
	if(0==inst->GetType()) return;
	if(0==inst->attrs) return;
	if(inst->GetAccessmode()>aplRO) return;

	int i, nAttr=inst->GetType()->all_attrs.GetSize();
	for(i=0; i<nAttr; i++)
	{
		CaplInstance::CaplValueDefinition *attrdef=&(inst->attrs[i]);
		if(0==attrdef) return;
		if(aplINSTANCE==attrdef->value.type)
		{
			CaplInstance *instval=attrdef->value.instval;
			if(0==instval) continue;
			
			if(stdExtent.end() == stdExtent.find(instval))
			{
				stdExtent.insert(instval);
				if(bRevursive) GetReferenceReqursive(instval,stdExtent,bRevursive);
			}
		}
		else if(aplAGGR==attrdef->value.type)
		{
			CaplAggr *aggrval=attrdef->value.aggrval;
			if(0!=aggrval)
			{
				int k;
				for(k=0;k<aggrval->GetSize();k++)
				{
					CaplValue *val=aggrval->GetByIndex(k);
					if(0!=val)
					{
						if(aplINSTANCE==val->type)
						{
							CaplInstance *instval=val->instval;
							if(0==instval) continue;
							if(stdExtent.end() == stdExtent.find(instval))
							{
								stdExtent.insert(instval);
								if(bRevursive) GetReferenceReqursive(instval,stdExtent,bRevursive);
							}
						}
					}
				}
			}
		}
	}
}


// Удаляет из ext все instance, на которые ссылается хотя бы один из ext (оставляет только корневые)
/// !!! Если в структуре значений есть циклы, то все элементы циклов в рещультат не попадают
bool CaplStepData::RemoveNonRootInstances(aplExtent &ext)
{
	if(0==ext.GetSize()) return true;

	int i;
	// 1. Формируем список тех, на которые вообще есть ссылки
	std::set<CaplInstance*> stdExtent;
	for(i=0; i<ext.GetSize(); i++) GetReferenceReqursive(ext[i], stdExtent, false);
	
	// 2. Собираем корневые (на которые нет ссылок)
	aplExtent extRoot;  extRoot.Unique=false;
	for(i=0; i<ext.GetSize(); i++)
	{
		CaplInstance *inst=ext[i];
		if(0==inst) continue;
		if(stdExtent.end() == stdExtent.find(inst)) extRoot.Add(inst);
	}

	/*
	// Теперь ищем instance, которые затерялись в циклических ссылках
	// (на них (по цепочке) не ссылается никто из extRoot)
	// 3. Формируем список тех, на которые по рекурсивно ссылаются из extRoot
	std::set<CaplInstance*> stdExtentFromRoot;
	for(i=0; i<extRoot.GetSize(); i++) GetReferenceReqursive(extRoot[i], stdExtentFromRoot, true);

	// 4. Формируем итоговый массив
	aplExtent extResult; extResult.Unique=false;
	for(i=0; i<ext.GetSize(); i++)
	{
		CaplInstance *inst=ext[i];
		if(0==inst) continue;
		if(stdExtentFromRoot.end() == stdExtentFromRoot.find(inst)) extResult.Add(inst);
	}
	stdExtentFromRoot.clear();

	aplExtent extTest;
	for(i=0; i<extResult.GetSize(); i++)
	{
		CaplInstance *inst=extResult[i];
		if(0==inst) continue;
		bool bFound=false;
		int j;
		for(j=0; j<extRoot.GetSize(); j++)
		{
			if(extRoot[j]==inst) {bFound=true; break;}
		}

		if(!bFound) 
			extTest.Add(inst);
	}*/

	// Копируем результат в результат
	ext.Clear();
	bool bOldUnique=ext.Unique;
	ext.Unique=false;
	for(i=0; i<extRoot.GetSize(); i++) ext.Add(extRoot[i]);
	ext.Unique=bOldUnique;

	stdExtent.clear();
	
	return true;
}
