// JSON.cpp 

#include "StdAfx.h"

#include "aplValue.h"
#include "StepData.h"

// Особая функция создания инстанса при разборе JSON в сервере oracle/postgre
// Нужна для правильной индексации нового инстанса в оракловом/постгресном сервере
CaplInstance* (*ExtCreateInstance)(CaplStepData* data, CaplEntity *ent, apl_id id, CaplInstance* ext_inst) = 0;
// функция чтения из БД типов инстансов при разборе JSON в сервере oracle/postgre
bool (*ExtGetType)(CaplStepData* data, aplExtent &ext, CString &list_inst) = 0;

void AFX_EXT_API SetJsonCreateInst(CaplInstance* (*_ExtCreateInstance)(CaplStepData* data, CaplEntity *ent, apl_id id, CaplInstance* ext_inst))
{
	ExtCreateInstance = _ExtCreateInstance;
}

void AFX_EXT_API SetJsonGetType(bool (*_ExtGetType)(CaplStepData* data, aplExtent &ext, CString &list_inst))
{
    ExtGetType = _ExtGetType;
}

////////////////////////////////////////////////////////////
// классы и структуры для работы с JSON
//

#define PRINT_ERROR_I_THIS(errmess_fmt, ...) do{CString str_tmp_d; str_tmp_d.Format(errmess_fmt, ##__VA_ARGS__); err_str += str_tmp_d; err_str += this->GetPos();}while(0)
#define RETURN_ERROR_I_THIS(errmess_fmt, ...) do{PRINT_ERROR_I_THIS(errmess_fmt, ##__VA_ARGS__); return 0;}while(0)
#define PRINT_ERROR_I(p_json_ev, errmess_fmt, ...) do{CString str_tmp_d; str_tmp_d.Format(errmess_fmt, ##__VA_ARGS__); err_str += str_tmp_d; if(p_json_ev!=0){err_str += p_json_ev->GetPos();}}while(0)
#define RETURN_ERROR_I(p_json_ev, errmess_fmt, ...) do{PRINT_ERROR_I(p_json_ev, errmess_fmt, ##__VA_ARGS__); return 0;}while(0)

#define TEXT_LINE_POS _T(" (line %i pos %i)")
#define PRINT_ERROR(errmess_fmt, ...) do{CString str_tmp_d; str_tmp_d.Format(CString(errmess_fmt)+TEXT_LINE_POS, ##__VA_ARGS__, line, pos_str); err_str += str_tmp_d;}while(0)
#define RETURN_ERROR(errmess_fmt, ...) do{PRINT_ERROR(errmess_fmt, ##__VA_ARGS__); return false;}while(0)


SJsonVal::SJsonVal()
{
	type = SJT_NOTYPE; ival=0;
	json_line = 0;
	json_pos = 0;
}

SJsonVal::~SJsonVal(){Clear();}

void SJsonVal::Clear(bool with_arr)
{
    // объекты надо очищать до обнуления rval - bval - ival, это ж union. Иначе указатели в object и sval потеряются
    switch (type)
    {
    case SJT_STRING:
        if (sval!=0)
        {
            delete []sval;
            sval=0;
        }
        break;
    case SJT_OBJECT:
        if(object!=0)
        {
            delete object;
            object=0;
        }
        break;
    default:
            // Ничего не делаем, кусок нужен чтобы gcc не ругался
        break;
    }
    rval = 0;
    bval = false;
    ival = 0;
	ival64 = 0;
    if(with_arr)
        array.Clear();

    // для массивов пофиг - AddArrayEl снова установит тип массив
    type=SJT_NOTYPE;

// 	json_line = 0;
// 	json_pos = 0;
}

void SJsonVal::Set(const CString &val)
{
	Clear();
	int k = val.GetLength() + 1;
	sval = new TCHAR[k];
	memcpy(sval, LPCTSTR(val), k * sizeof(TCHAR));
	type = SJT_STRING;
}

#ifdef QSTRING_H
void SJsonVal::Set(const QString &val)
{
    Clear();
    int k = val.size() + 1;
    sval = new TCHAR[k];
    memcpy(sval, QS2CS(val), k * sizeof(TCHAR));
    type = SJT_STRING;
}
#endif

void SJsonVal::Set(LPCTSTR val)
{
	Clear();
	if(val==0)return;
	int k=_strlen(val)+1;
	sval=new TCHAR[k];
	memcpy(sval, val, k * sizeof(TCHAR));
	type=SJT_STRING;
}

void SJsonVal::Set(int val)
{
	Clear();
	ival = val;
	type = SJT_INT;
}
void SJsonVal::Set(__int64 val)
{
	Clear();
	ival64 = val;
	type = SJT_INT64;
}
void SJsonVal::Set(unsigned long val)
{
	Clear();
	ival = (int)val;
	type = SJT_INT;
}
void SJsonVal::Set(double val)
{
	Clear();
	rval = val;
	type = SJT_REAL;
}
void SJsonVal::Set(bool val)
{
	Clear();
	bval = val;
	type = SJT_BOOL;
}
void SJsonVal::Set(SJsonEl* val)
{
	Clear();
	object = val;
	type = SJT_OBJECT;
}
void SJsonVal::Set(SJsonEl& val)
{
	Clear();
	object = new SJsonEl();
	object->Set(val);
	type = SJT_OBJECT;
}

void SJsonVal::SetNull()
{
	Clear();
	type = SJT_NULL;
}

void SJsonVal::Set(SJsonVal& val)
{
	switch(val.type)
	{
	case SJT_NULL: 
		type = SJT_NULL;
		break;
	case SJT_INT: Set(val.ival); break;	
	case SJT_INT64: Set(val.ival64); break;	
	case SJT_REAL: Set(val.rval); break;	
	case SJT_BOOL: Set(val.bval); break;	
	case SJT_STRING: Set(val.sval); break;	
	case SJT_OBJECT: Set(*(val.object)); break;	
	case SJT_ARRAY: 
		Clear(true);
		for(int i=0;i<val.array.GetSize();i++)		
		{
			AddArrayEl(*((SJsonVal*)val.array.GetAt(i)));
		}
		type = SJT_ARRAY;
		break;	
    default:
        // Ничего не делаем, кусок нужен чтобы gcc не ругался
        break;
    }
	SetPos(val.json_line, val.json_pos);
}

#define ADD_ARR_VAL_BODY \
{ \
	Clear(false); \
	SJsonVal* arrval = new SJsonVal(); \
	arrval->Set(val); \
	array.Add(arrval); \
	type = SJT_ARRAY; \
}

void SJsonVal::AddArrayEl(const CString &val) ADD_ARR_VAL_BODY
#ifdef QSTRING_H
void SJsonVal::AddArrayEl(const QString &val){AddArrayEl(QS2CS(val));}
#endif
void SJsonVal::AddArrayEl(LPCTSTR val) ADD_ARR_VAL_BODY
void SJsonVal::AddArrayEl(int val) ADD_ARR_VAL_BODY
void SJsonVal::AddArrayEl(__int64 val) ADD_ARR_VAL_BODY
void SJsonVal::AddArrayEl(double val) ADD_ARR_VAL_BODY
void SJsonVal::AddArrayEl(bool val) ADD_ARR_VAL_BODY
void SJsonVal::AddArrayEl(SJsonEl& val) ADD_ARR_VAL_BODY
void SJsonVal::AddArrayEl(SJsonVal& val) ADD_ARR_VAL_BODY
// Внимание! Объект по указателю val будет удален в деструкторе этого класса!
void SJsonVal::AddArrayEl(SJsonEl* val)ADD_ARR_VAL_BODY
void SJsonVal::AddZeroArrayEl()
{
	Clear(false);
	type = SJT_ARRAY;
}

void SJsonVal::InsertArrayEl(const CString &val) 
{
	Clear(false);
	SJsonVal* arrval = new SJsonVal();
	arrval->Set(val);
	array.Insert(0, arrval);
	type = SJT_ARRAY;
}
#ifdef QSTRING_H
void SJsonVal::InsertArrayEl(const QString &val){InsertArrayEl(QS2CS(val));}
#endif


int SJsonVal::GetArraySize()
{
	return array.GetSize();
}

#define GET_ARR_EL_BODY \
{ \
	if(index >= array.GetSize()) return false; \
	SJsonVal* arrval = array.GetAt(index); \
	if(arrval == 0) return false; \
	return arrval->Get(val); \
}

bool SJsonVal::GetArrayEl(int index, int &val)GET_ARR_EL_BODY
bool SJsonVal::GetArrayEl(int index, __int64 &val)GET_ARR_EL_BODY
bool SJsonVal::GetArrayEl(int index, double &val)GET_ARR_EL_BODY
bool SJsonVal::GetArrayEl(int index, bool &val)GET_ARR_EL_BODY
bool SJsonVal::GetArrayEl(int index, CString &val)GET_ARR_EL_BODY
#ifdef QSTRING_H
bool SJsonVal::GetArrayEl(int index, QString &val)
{
    CString csval;
    if(!GetArrayEl(index, csval)) return false;
    val = CS2QS(csval);
    return true;
}
#endif

bool SJsonVal::GetArrayEl(int index, SJsonEl &val)GET_ARR_EL_BODY
bool SJsonVal::GetArrayEl(int index, SJsonVal &val)GET_ARR_EL_BODY


bool SJsonVal::Get(SJsonVal &val)
{
	switch(type)
	{
	case SJT_NULL:
		val.type = SJT_NULL;
		break;
	case SJT_INT: val.Set(ival); break;	
	case SJT_INT64: val.Set(ival64); break;	
	case SJT_REAL: val.Set(rval); break;	
	case SJT_BOOL: val.Set(bval); break;	
	case SJT_STRING: val.Set(sval); break;	
	case SJT_OBJECT: val.Set(*(object)); break;	
	case SJT_ARRAY: 
		val.Clear(true);
		for(int i=0;i<array.GetSize();i++)		
		{
			val.AddArrayEl(*((SJsonVal*)array.GetAt(i)));
		}
		break;	
    default:
        // Ничего не делаем, кусок нужен чтобы gcc не ругался
        break;
    }
	val.SetPos(json_line, json_pos);
	return true;
}

bool SJsonVal::Get(CString &val)
{
	val.Empty();
	if(type != SJT_STRING) return false;
	val = sval;
	return true;
}

#ifdef QSTRING_H
    bool SJsonVal::Get(QString &val)
    {
        val.clear();
        if(type != SJT_STRING) return false;
        val = CS2QS(sval);
        return true;
    }
#endif

bool SJsonVal::Get(int &val)
{
	val = 0;
	if(type != SJT_INT) return false;
	val = ival;
	return true;
}

bool SJsonVal::Get(__int64 &val)
{
	val = 0;
	if(type != SJT_INT64) return false;
	val = ival64;
	return true;
}

bool SJsonVal::Get(double &val)
{
	val = 0;
	if(type != SJT_REAL) return false;
	val = rval;
	return true;
}

bool SJsonVal::Get(bool &val)
{
	val = false;
	if(type != SJT_BOOL) return false;
	val = bval;
	return true;
}

bool SJsonVal::Get(SJsonEl &val)
{
	val.Clear();
	if(type != SJT_OBJECT) return false;
	val.Set(*object);
	return true;
}

SJsonEl* SJsonVal::GetEl()
{
	if(type != SJT_OBJECT) return 0;
	return object;
}

void SJsonVal::SetPos(int _json_line, int _json_pos)
{
	json_line = _json_line;
	json_pos = _json_pos;
}

CString SJsonVal::GetPos()
{
	CString str; 
	str.Format(_T(" (line %i pos %i)"), json_line, json_pos); 
	return str;
}



#define ADD_TABS {for(int t=0;t<level; t++)str_rep += _T("\t");}
#define ADD_LINE_TABS {str_rep += _T("\n");ADD_TABS}

void StringOutCorrect(CString &str)
{
	str.Replace(_T("\\"), _T("\\\\"));
	str.Replace(_T("\""), _T("\\\""));
	str.Replace(_T("\n"),_T("\\n"));
	str.Replace(_T("\r"),_T(""));

}

CString SJsonVal::Print(int &level)
{
	CString str_rep = _T(""), str;
	SJsonVal *v_el;
	bool prev_el_arr = false;

	switch(type)
	{
	case SJT_STRING:
		str = sval;
		if(str == _T(""))
			return _T("");
		StringOutCorrect(str);
		str_rep += _T("\"");
		str_rep += str;
		str_rep += _T("\"");
		break;
	case SJT_INT:
		str.Format(_T("%i"), ival);
		str_rep+=str;
		break;
	case SJT_INT64:
#ifdef __linux__
		str.Format(_T("%lld"), ival64);
#else
		str.Format(_T("%I64d"), ival64);
#endif
		str_rep+=str;
		break;
	case SJT_REAL:
		str.Format(_T("%1.15G"),rval); 
		str_rep+=str;
		break;
	case SJT_BOOL:
		if(bval) str=_T("true"); else str=_T("false");
		str_rep+=str;
		break;
	case SJT_OBJECT:
		if(!object->PrintInternal(str_rep, level))
			return _T("");
		break;
	case SJT_ARRAY:
		str_rep += _T("[");
		level++;
		ADD_LINE_TABS
			for(int i=0; i<array.GetSize(); i++)
			{
				v_el = array.GetAt(i);
				if(v_el!=0)
				{
					str = v_el->Print(level);
					if(str == _T(""))
					{
						continue;
					}
					else if(prev_el_arr)
					{
						str_rep += _T(",\n");
						ADD_TABS
					}
					str_rep += str;
					prev_el_arr = true;
				}
			}
			level--;
			ADD_LINE_TABS
				str_rep += _T("]");
			break;
	case SJT_NULL:
		str_rep += _T("null");
		break;
	case SJT_NOTYPE:
	default:
		return _T("");
		break;
	}

	return str_rep;
}


SListAttrsNextLevel gl_QD_no_attrs;
SListAttrsNextLevel gl_QD_all_attrs_in_tree;
SListAttrsNextLevel* SJsonEl::flag_QD_no_attrs(&gl_QD_no_attrs);
SListAttrsNextLevel* SJsonEl::flag_QD_single_level_attrs(0);
SListAttrsNextLevel* SJsonEl::flag_QD_all_attrs_in_tree(&gl_QD_all_attrs_in_tree);


SJsonEl::SJsonEl()
{
	json_line = 0;
	json_pos = 0;
	flag_warning = false;
	m_ErrDescr = _T("");

	m_bNoSaveEmptyAttr=true;
}

SJsonEl::SJsonEl(const SJsonEl &prev)
{
    Set(prev);
}

SJsonEl::~SJsonEl(){Clear();}

void SJsonEl::Clear()
{
	for(int i=0; i<key_vals.GetSize(); i++)
		delete ((SJsonVal*)key_vals.GetAt(i)->val);

	key_vals.Clear();
	key_vals_order.Clear();
// 	json_line = 0;
// 	json_pos = 0;
	m_ErrDescr = _T("");
}

void SJsonEl::Set(const SJsonEl &el)
{
    if (this == &el) return;

	Clear();

	SJsonVal *val;

	for(int i=0; i<el.key_vals.GetSize(); i++)
	{
		val = (SJsonVal *)el.key_vals.GetAt(i)->val;
		AddKeyVal(el.key_vals.GetAt(i)->str, *val);
	}
	SetPos(el.json_line, el.json_pos);
}

// заполнение параметра с текстом ошибки в блоке JSON
// варнинги добавляются в конец, ошибки - в начало блока опиисания
void SJsonEl::SetErrDescr(LPCTSTR text, bool flag_err)
{
	SJsonVal *warnings = GetKeyVal(_T("error_description"));
	if(warnings==0)
	{
		warnings = new SJsonVal();
		AddKeyVal(_T("error_description"), warnings);
	}
	if(flag_err)
	{
		warnings->InsertArrayEl(text);
	}
	else
	{
		warnings->AddArrayEl(text);
	}
	flag_warning = !flag_err;
	m_ErrDescr = text;
}
#ifdef QSTRING_H
void SJsonEl::SetErrDescr(QString qsMess, bool flag_err)
{
    SetErrDescr(QS2CS(qsMess), flag_err);
}
void SJsonEl::SetErrDescr(QStringList qslMess, bool flag_err)
{
    if(qslMess.size()==2)
        SetErrDescr(QS2CS(qslMess[1]), flag_err);
    else
        SetErrDescr(QS2CS(qslMess[0]), flag_err);
}
#endif


void SJsonEl::SetPos(int _json_line, int _json_pos)
{
	json_line = _json_line;
	json_pos = _json_pos;
}

CString SJsonEl::GetPos()
{
	CString str; 
	str.Format(_T(" (line %i pos %i)"), json_line, json_pos); 
	return str;
}

void SJsonEl::AddHeader(CaplStepData* data, bool all_dict)
{
	Clear();
	if(data==0) return;
	AddKeyVal(_T("format"),_T("apl_json_1"));
	if(all_dict)
	{
		CaplStrMap map;
		data->GetDictionaryDescription(map);
		SJsonVal jsval;
		for(int i=0; i<map.GetSize(); i++)
		{
			SJsonEl* el=new SJsonEl();
			el->AddKeyVal(map.GetAt(i)->str, (int)map.GetAt(i)->val);
			jsval.AddArrayEl(el);
		}
		AddKeyVal(_T("dictionary"), jsval);
	}
	else
		AddKeyVal(_T("dictionary"), data->m_CurSchema);

}

#define ADD_KEY_VAL_BODY \
{ \
    SJsonVal* my_val = (SJsonVal*)key_vals.GetP(key); \
	if(my_val == 0){my_val = new SJsonVal(); key_vals.Add(key, my_val); key_vals_order.Add(new JSNStrVal(key, my_val));} \
    my_val->Set(val); \
}

void SJsonEl::AddKeyVal(LPCTSTR key, int val) ADD_KEY_VAL_BODY
void SJsonEl::AddKeyVal(LPCTSTR key, __int64 val) ADD_KEY_VAL_BODY
void SJsonEl::AddKeyVal(LPCTSTR key, unsigned long val) ADD_KEY_VAL_BODY
void SJsonEl::AddKeyVal(LPCTSTR key, double val) ADD_KEY_VAL_BODY
void SJsonEl::AddKeyVal(LPCTSTR key, bool val) ADD_KEY_VAL_BODY
void SJsonEl::AddKeyVal(LPCTSTR key, LPCTSTR val) ADD_KEY_VAL_BODY
void SJsonEl::AddKeyVal(LPCTSTR key, const CString &val) ADD_KEY_VAL_BODY
#ifdef QSTRING_H
void SJsonEl::AddKeyVal(LPCTSTR key, const QString &val){ AddKeyVal(key, QS2CS(val));}
#endif
void SJsonEl::AddKeyVal(LPCTSTR key, SJsonEl& val) ADD_KEY_VAL_BODY
void SJsonEl::AddKeyVal(LPCTSTR key, SJsonVal& val) ADD_KEY_VAL_BODY

// Внимание! Объект по указателю val будет удален в деструкторе этого класса!
void SJsonEl::AddKeyVal(LPCTSTR key, SJsonVal* val)
{
	if(val==0) return;
	SJsonVal* my_val = (SJsonVal*)key_vals.GetP(key);
	if(my_val != 0)
	{
		if(my_val == val)
			return;
		// Если значение для такого ключа есть, то заменяем его в общем списке значений
		for(int i=0; i<key_vals_order.GetSize(); i++)
		{
			if(key_vals_order.GetAt(i)->val == my_val)
			{
				key_vals_order.GetAt(i)->val = val;
			}
		}
		// и в упорядоченном списке ключей-значений
		key_vals.Add(key, val);
		delete my_val;
	}
	else
	{
		key_vals.Add(key, val);
		key_vals_order.Add(new JSNStrVal(key, val));
	}
}


bool SJsonEl::GetKeyVal(LPCTSTR key, SJsonVal &val) 
{
	val.Clear();
	SJsonVal* my_val = (SJsonVal*)key_vals.GetP(key);
	if(my_val==0) return false;
	return my_val->Get(val);
}

#define GET_KEY_VAL_BODY \
{ \
	SJsonVal* my_val = (SJsonVal*)key_vals.GetP(key); \
	if(my_val==0) return false; \
	return my_val->Get(val); \
}


bool SJsonEl::GetKeyVal(LPCTSTR key, CString &val) GET_KEY_VAL_BODY
#ifdef QSTRING_H
void SJsonEl::GetKeyVal(LPCTSTR key, QString &val)
{
    CString csval;
    GetKeyVal(key, csval);
    val = CS2QS(csval);
}
#endif

bool SJsonEl::GetKeyVal(LPCTSTR key, int &val) GET_KEY_VAL_BODY
bool SJsonEl::GetKeyVal(LPCTSTR key, __int64 &val) GET_KEY_VAL_BODY
bool SJsonEl::GetKeyVal(LPCTSTR key, double &val) GET_KEY_VAL_BODY
bool SJsonEl::GetKeyVal(LPCTSTR key, bool &val) GET_KEY_VAL_BODY
bool SJsonEl::GetKeyVal(LPCTSTR key, SJsonEl& val) GET_KEY_VAL_BODY


bool SJsonEl::Print(CString &report)
{
	report = _T("");
	int level=0;

	return PrintInternal(report, level);
}

bool SJsonEl::PrintInternal(CString &report, int &level)
{
	SJsonVal *val;
	CString key, str, str_rep, str_key_attrs(_T(""));
	int count = 0;
	str_rep = _T("{\n");
	level++;
	for(int i=0; i< key_vals_order.GetSize(); i++)
	{
		key = key_vals_order.GetAt(i)->str;
		val = (SJsonVal*)key_vals_order.GetAt(i)->val;

		if(val == NULL)
			continue;
		else
			str = val->Print(level);

		if(str== _T(""))
			continue;

		if(key==_T("attributes"))
		{
			StringOutCorrect(key);
			str_key_attrs = _T("\"") + key + _T("\":") + str;
			continue;
		}

		if(count > 0)
			str_rep += _T(",\n");

		ADD_TABS
		StringOutCorrect(key);
		str_rep += _T("\"") + key + _T("\":") + str;
		count++;
	}
	if(!str_key_attrs.IsEmpty())
	{
		if(count > 0)
			str_rep += _T(",\n");

		ADD_TABS
		str_rep += str_key_attrs;
	}
	level--;

	if(count > 1)ADD_LINE_TABS
		str_rep += _T("}");

	if(count == 0)
		report = _T("{ }");
	else
		report += str_rep;

	return true;
}


// Вспомогательная функция; сохраняет значение атрибута в элемент JSON
// если есть следующие запросы - их атрибуты разворачивает в дереве
bool SJsonVal::PrintAttrVal2Json(CaplValue &value, CaplAttr* attr, SListAttrsNextLevel* QD_curr,
							pCaplInstance *instmap, int instmapsize, mapOfInst* pExported)
{
	CString str;
	Clear(true);
	SJsonEl j_el;
	SListAttrsNextLevel* QD_next(QD_curr);

	if(attr == 0)
		return false;

	switch (value.type)
	{
	case aplINTEGER: 
		Set(value.ival); 
		break;
	case aplREAL: 
		Set(value.rval); 
		break;
	case aplBOOL: 
		Set(value.bval); 
		break;
	case aplLOGICAL: 
		switch(value.lval)
		{
		case APL_FALSE: Set(false);break;
		case APL_TRUE: Set(true);break;
		default:Set(0);break;
		}
		break;
	case aplSTRING: 
		//REST пустые строки можно не указывать, но это на потом обдумать
		Set(value.sval); 
		break;

	case aplINSTANCE: 
		if(value.instval==0 || value.instval->GetType()==0)
		{
			SetNull();
		}
		else if(instmap==0)
		{
			if(QD_curr == SJsonEl::flag_QD_no_attrs)
			{
				SetNull();
				break;
			}
			if(QD_curr == SJsonEl::flag_QD_single_level_attrs) 
				// Если параметры вывода атрибутов не заданы, то чтобы не зацикливаться, отключаем вывод 
				//атрибутов на следующих уровнях.
				QD_next = SJsonEl::flag_QD_no_attrs; 
			else if(QD_curr == SJsonEl::flag_QD_all_attrs_in_tree) 
			{
				if(pExported==0)
					// Защита от некорректного значения. Атрибуты будут выведены только на одном уровне, 
					//а потом будут выводиться только свойства инстансов
					QD_next = SJsonEl::flag_QD_single_level_attrs;
				else 
				{
					mapOfInst::iterator it = pExported->find(value.instval);
					if(it==pExported->end())
						(*pExported)[value.instval]=0; // Добавляем инстанс с массив и выводим его атрибуты
					else
						QD_next = SJsonEl::flag_QD_no_attrs;  // атрибуты инстанса уже выведены, выводим только свойства инстанса
				}
			}
			j_el.PrintInst2Json(value.instval, QD_next, aplNOT_DEFINED, 0, 0, pExported);
			Set(j_el);
		}
		else
		{
			int index=aplQuickFindP((void**)instmap, instmapsize, value.instval);
			if(index==-1){	SetNull();}
			else{ Set(index);}
		}
		break;
	case aplBINARY: 
		{
			if(value.binval!=0)
			{
				str.Format(_T("SIZE = %i"), value.binval->m_Size);
				Set(str); 
			}
		}
		break;
	case aplINTEGRATEDBIN: 
		{
			str.Format(_T("%i"), value.intbinval->blobId);
			Set(str); 
		}
		break;

	case aplAGGR: 
		for (int i=0;i<value.aggrval->GetSize();i++)
		{
			CaplValue val1;
			SJsonVal j_attr_val1;
			value.aggrval->GetByIndex(i, val1);
			j_attr_val1.PrintAttrVal2Json(val1, attr, QD_curr, instmap, instmapsize, pExported);
			AddArrayEl(j_attr_val1);
		}
		break;
    default:
        // Ничего не делаем, кусок нужен чтобы gcc не ругался
        break;
    }
	return true;
}

bool SJsonEl::AddInst2JsonRoot(CaplInstance* inst, SListAttrsNextLevel* QD_curr,
					  aplAccessModeType access, pCaplInstance *instmap, int instmapsize)
{
	if(inst == 0){SetErrDescr(APL_T("Пустой объект inst"), true); return false;}

	SJsonVal *inst_array = GetKeyVal(_T("instances"));
	if(inst_array==0)
	{
		inst_array = new SJsonVal();
		// память из inst_array будет очищена внутри деструктора JsonRoot
		AddKeyVal(_T("instances"), inst_array);
	}
	else if(inst_array->GetType() != SJT_ARRAY)
	{
		CString strErr;
		strErr.Format(APL_T("В параметр 'instances' типа '%s' невозможно добавить описание инстанса. Параметр 'instances' должен иметь тип '%s'"),
			(LPCTSTR)GetSJsonTypeName(inst_array->GetType()), (LPCTSTR)GetSJsonTypeName(SJT_ARRAY));
		SetErrDescr(strErr, true);
		return false;
	}

	SJsonEl *j_el = new SJsonEl();
	mapOfInst* pExported(0);
	if(QD_curr==SJsonEl::flag_QD_all_attrs_in_tree)
		pExported = new mapOfInst();
	if(!j_el->PrintInst2Json(inst, QD_curr, access, instmap, instmapsize, pExported))
	{
		SetErrDescr(j_el->GetErrDescr(), true);
		delete j_el;
		if(pExported!=0) delete pExported;
		return false;
	}
	if(pExported!=0) delete pExported;
	inst_array->AddArrayEl(j_el); // память из j_el будет освобождена в деструкторе inst_array
	return true;
}


// Функция корневого элемента JSON.
// Сохраняет инстанс в элемент JSON и добавляет его в массив instances
// Заполняет только минимальную информацию об инстансе: id, тип, индекс 
bool SJsonEl::AddInst2JsonRoot(CaplInstance* inst, int index)
{
	if(inst == 0){SetErrDescr(APL_T("Пустой объект inst"), true); return false;}

	SJsonVal *inst_array = GetKeyVal(_T("instances"));
	if(inst_array==0)
	{
		inst_array = new SJsonVal();
		// память из inst_array будет очищена внутри деструктора JsonRoot
		AddKeyVal(_T("instances"), inst_array);
	}
	else if(inst_array->GetType() != SJT_ARRAY)
	{
		CString strErr;
		strErr.Format(APL_T("В параметр 'instances' типа '%s' невозможно добавить описанеи инстанса. Параметр 'instances' должен иметь тип '%s'"),
			(LPCTSTR)GetSJsonTypeName(inst_array->GetType()), (LPCTSTR)GetSJsonTypeName(SJT_ARRAY));
		SetErrDescr(strErr, true);
		return false;
	}

	SJsonEl *j_el = new SJsonEl();
	j_el->AddKeyVal(_T("id"), inst->GetId());
	if(inst->GetType() == 0)
		j_el->AddKeyVal(_T("type"), 0);
	else
		j_el->AddKeyVal(_T("type"), inst->GetType()->name);

	j_el->AddKeyVal(_T("index"), index);

	inst_array->AddArrayEl(j_el); // память из j_el будет освобождена в деструкторе inst_array
	return true;
}



// Сохраняет инстанс в элемент JSON
bool SJsonEl::PrintInst2Json( CaplInstance* inst, SListAttrsNextLevel* QD_curr, aplAccessModeType access_in,
																pCaplInstance *instmap, int instmapsize, mapOfInst* pExported)
{
	aplAccessModeType c_access;
	Clear();
	if(inst == 0){SetErrDescr(APL_T("Пустой объект inst"), true); return false;}

	AddKeyVal(_T("id"), inst->GetId());
	if(inst->GetType() == 0)
		AddKeyVal(_T("type"), 0);
	else
		AddKeyVal(_T("type"), inst->GetType()->name);

	if(instmap!=0)
	{
		int index=aplQuickFindP((void**)instmap, instmapsize, inst);
		if(index==-1){	Clear(); return false;}
		else{ AddKeyVal(_T("index"), index);}
	}

	if(access_in == aplNOT_DEFINED)
	{
        if(QD_curr != 0 && QD_curr != SJsonEl::flag_QD_all_attrs_in_tree &&
						QD_curr != SJsonEl::flag_QD_single_level_attrs && QD_curr != SJsonEl::flag_QD_no_attrs)
			c_access = QD_curr->GetInstAccess(inst);
		else
			c_access = inst->GetAccessmode();
	}
	else
		c_access = access_in;

	AddKeyVal(_T("access"), GetNameAccess(c_access, false, false));

	// Для режима rest атрибуты выводим только при наличии доступа
	// Для режима data атрибуты выводим всегда
	if(inst->attrs!=0 && (c_access < aplNO || instmap != 0))
	{
		//attrs-array SJsonVal attrs_array;
		SJsonEl *p_attrs_el;
		SJsonVal *p_val, *p_attrs_val;
		CaplEntity *ent=inst->GetType();
		CaplAttr* attr;
		SListAttrsNextLevel* QD_next(SJsonEl::flag_QD_single_level_attrs);

		if(pExported != 0)
		{
			mapOfInst::iterator it = pExported->find(inst);
			if(it==pExported->end())
				(*pExported)[inst]=0; // Добавляем инстанс с массив и выводим его атрибуты
			else
				return true;  // атрибуты инстанса уже выведены, выводим только свойства инстанса

		}
		if(instmap==0 && QD_curr==SJsonEl::flag_QD_no_attrs)
			return true;  // атрибуты инстанса выводить не надо

		if(ent!=0)
		{
			if(instmap != 0)
				QD_next = SJsonEl::flag_QD_single_level_attrs;

			p_attrs_el = new SJsonEl();
			for(int j=0;j<ent->all_attrs.Size;j++)
			{
				attr = ent->all_attrs[j];

				if(instmap==0)
				{
					// Для режима rest и вывода инстансов деревьями:
					//		- если QD_curr равен SJsonEl::flag_QD_no_attrs - это вызов с предыдущего уровня с не заданным QD_curr. Для таких 
					// инстансов атрибуты не выводим вообще. Должно отсекаться проверкой выше, тут защита от дурака и чекеров кода.
					// 		- если QD_curr равен flag_QD_all_attrs_in_tree, то передаем проверку в PrintAttrVal2Json, и если инстанс встретился 
					// первый раз, то выводим все его атрибуты + инстансы в виде вложенных объектов JSON, а если не в первый - то 
					// атрибуты не выводим. Проверка по pExported.
					//		- если QD_curr равен flag_QD_single_level_attrs - выводим все значения атрибутов текущего инстанса, инстансы 
					// выводим, но их атрибуты уже не выводим вообще
					//		- если задан обычный объект, то ищем в нем флаг all_attrs или флаги вывода для конкретных атрибутов
					if(QD_curr==SJsonEl::flag_QD_no_attrs)
						continue;
					else if(QD_curr==SJsonEl::flag_QD_all_attrs_in_tree)
						QD_next = SJsonEl::flag_QD_all_attrs_in_tree;
					else if(QD_curr!=SJsonEl::flag_QD_single_level_attrs)
					{
						// параметры вывода атрибута attr
						QD_next = (SListAttrsNextLevel*)QD_curr->map_child_attrs.GetP(attr->name);
						// если QD_next для атрибута attr не задан и для текущего уровня не установлен флаг all_attrs - не выводим этот атри
						// если же есть all_attrs - выводим все атрибуты
						// если же all_attrs нету - выводим только те, для которых определен следующий уровень
						// Ну и для следующего уровня будет или валидный объект QD с параметрами, или вывод атрибутов только одного уровня
						if(QD_next == SJsonEl::flag_QD_single_level_attrs && !QD_curr->all_attrs)
							continue;
					}
				}

				CaplValue &attrval=inst->attrs[j].value;

				if(m_bNoSaveEmptyAttr) // ЯАИ: Проверка чтобы не выгружать пустые атрибуты
				{
					if(aplNOTYPE==attrval.type) continue;
					if(aplSTRING==attrval.type)
					{
						if(0==attrval.sval) continue;
						if(_T('\0')==attrval.sval[0]) continue;
					}
					else if(aplINSTANCE==attrval.type)
					{
						if(0==attrval.instval) continue;
						if(true==attrval.instval->IsDeleted()) continue;
						if(0==attrval.instval->GetType()) continue;
					}
					else if(aplAGGR==attrval.type)
					{
						if(0==attrval.aggrval) continue;
						if(0==attrval.aggrval->GetSize()) continue;
					}
				}

				p_val = new SJsonVal();
				p_val->PrintAttrVal2Json(attrval, attr, QD_next, instmap, instmapsize, pExported);
				p_attrs_el->AddKeyVal(attr->name, p_val); // память переходит под управление j_attr_el
			}
			p_attrs_val = new SJsonVal();
			p_attrs_val->Set(p_attrs_el);
			AddKeyVal(_T("attributes"), p_attrs_val);
		}
	}

	return true;
}


// проматывает пробелы, табы и т.п. до первого значащего символа
// если доходит до конца строки - возвращает false
// если находит значащий символ - возвращает true, позиция pos указывает на этот символ
bool ScrollSpace(const CString &json, int &pos, int &line, int &pos_str)
{
    while(pos < (int)json.GetLength())
	{
		switch(json[pos])
		{
		case _T('\n'):
			line++;pos_str=0;
			break;
		case _T(' '):
		case _T('\r'):
		case _T('\t'):
			pos_str++;
			break;
		default:
			return true;
		}
		pos++;
	}
	return false;
}

CString GetJsonString(const CString &json, int &pos, CString &err_str, int &line, int &pos_str)
{
	CString str = _T("");
#ifndef _UNICODE
	CStringW strW = _T("");
	bool modeW=false;
#endif
	bool line_end = false;

	while(pos < json.GetLength())
	{
		int d=1;
		if(json[pos] == _T('\\'))
		{
			if(pos > json.GetLength()-2)
			{
				PRINT_ERROR(APL_T("Встретился одиночный символ экранирования."));
			}
			else if(json[pos+1] == _T('"') || json[pos+1] == _T('\\'))
			{
#ifndef _UNICODE
				if(modeW)strW+=json[pos+1];
				else str+=json[pos+1];
#else
				str+=json[pos+1];
#endif
			}
			else if(json[pos+1] == _T('u'))
			{
				if(pos > json.GetLength()-6)
				{
#ifndef _UNICODE
					if(modeW)strW+=json[pos+1];
					else str+=json[pos+1];
#else
					str+=json[pos+1];
#endif
				}
				else
				{
					CString cs_code=_T("0x")+json.Mid(pos+2,4);
					unsigned short uchar =  (unsigned short)_strtol(cs_code, NULL, 16);
					wchar_t wchar = (wchar_t)uchar;
#ifdef _UNICODE
					str+=wchar;
#else	
					if(!modeW)
					{
						modeW=true;
						strW = str;
					}
					strW += wchar;
#endif
					d+=4;
				}
			}
			else
			{
#ifndef _UNICODE
				if(modeW){strW+=json[pos]; strW+=json[pos+1];}
				else {str+=json[pos]; str+=json[pos+1];}
#else
				str+=json[pos]; str+=json[pos+1];
#endif
			}
			d++;
		}
		else if(json[pos] == _T('"'))
		{
			line_end = true;
		}
		else if(json[pos] == _T('\n'))
		{
			pos_str = 0;
			line++;
#ifndef _UNICODE
			if(modeW)strW+=json[pos];
			else str+=json[pos];
#else
			str+=json[pos];
#endif
		}
		else
		{
#ifndef _UNICODE
			if(modeW)strW+=json[pos];
			else str+=json[pos];
#else
			str+=json[pos];
#endif
		}

		pos+=d;
		pos_str+=d;
		if(line_end)
			break;
        if(pos>=(int)json.GetLength())
		{
			PRINT_ERROR(APL_T("Строка не имеет корректного окончания."));
		}
	}
#ifndef _UNICODE
	if(modeW)
		str = strW;
#endif
	return str;
}

bool SJsonEl::ParseText2JsonInternal(const CString &json, int &pos, CString &err_str, int &line, int &pos_str)
{
	Clear();
	if(json == _T("")) return false;
	if(json[pos] != _T('{')) return false;
	SetPos(line, pos_str);
	pos++;pos_str++;

	SJsonVal *p_val;

	// ищем ключ
	CString key = _T("");
	CString str;

    while(pos < (int)json.GetLength())
	{
		if(!ScrollSpace(json, pos, line, pos_str))
			RETURN_ERROR(APL_T("Некорректное завершение строки JSON."));

		if(json[pos] == _T('}'))
		{
			// элемент закончился, выходим на следующей позиции
			pos++; pos_str++;
			return true;
		}
		if(json[pos] != _T('"'))
		{
			RETURN_ERROR(APL_T("Ожидается начало ключа - символ '\"'."));
		}
		pos++;pos_str++; // внутрь надо войти на позиции, следующей после "
		key = GetJsonString(json, pos, err_str, line, pos_str);

		if(CheckKeyVal(key))
			RETURN_ERROR(APL_T("Дублирующийся ключ '%s'."), (LPCTSTR)key);

		if(!ScrollSpace(json, pos, line, pos_str))
			RETURN_ERROR(APL_T("Некорректное завершение строки JSON."));
		
		if(json[pos] != _T(':'))
			RETURN_ERROR(APL_T("Ожидается разделитель ключа '%s' и значения - символ ':'."), LPCTSTR(key));

		pos++;pos_str++; // внутрь надо войти на позиции, следующей после :

		p_val = new SJsonVal();
		if(p_val->GetJsonVal(json, pos, err_str, line, pos_str))
			AddKeyVal(key, p_val);
		else
		{	delete p_val; return false;}
			
		if(!ScrollSpace(json, pos, line, pos_str))
			RETURN_ERROR(APL_T("Не найдены символы ',' или '}'."));

		if(json[pos] == _T(','))
		{
			// ищем следующий ключ
			key = _T("");
			pos++;pos_str++;
			continue;
		}
		else if(json[pos] == _T('}'))
		{
			// элемент закончился, выходим на следующей позиции
			pos++; pos_str++;
			return true;
		}
		else 
		{
			RETURN_ERROR(APL_T("Ожидается разделитель значений ',' или окончание элемента '}'."));
		}
	}
	return true;
}


bool SJsonVal::GetJsonVal(const CString &json, int &pos, CString &err_str, int &line, int &pos_str)
{
	Clear(true);
	if(json == _T("")) return false;

	SJsonEl *p_el_int;
	SJsonVal j_val_int;
	CString str;

	if(!ScrollSpace(json, pos, line, pos_str))
	{
		RETURN_ERROR(APL_T("Некорректное завершение строки JSON. Ожидается значение."));
	}
	SetPos(line, pos_str);

	if(json[pos] == _T('{'))
	{
		// объект
		p_el_int = new SJsonEl();
		if(p_el_int->ParseText2JsonInternal(json, pos, err_str, line, pos_str))
		{
			Set(p_el_int);
			return true;
		}
		else
		{	delete p_el_int; return false;}
	}
	else if(json[pos] == _T('['))
	{
		// массив
		pos++;pos_str++; // внутрь надо войти на позиции, следующей после [
        while(pos < (int)json.GetLength())
		{
			if(!ScrollSpace(json, pos, line, pos_str))
			{
				RETURN_ERROR(APL_T("Некорректное завершение строки JSON. Ожидается элемент массива."));
			}

			if(j_val_int.GetJsonVal(json, pos, err_str, line, pos_str))
				AddArrayEl(j_val_int);
			else
				return false;

			if(!ScrollSpace(json, pos, line, pos_str))
			{
				RETURN_ERROR(APL_T("Некорректное завершение строки JSON. Ожидается окончание или следующий элемент массива."));
			}
			if(json[pos] == _T(','))
			{
				// ищем следующий элемент массива
				pos++;pos_str++;
				continue;
			}
			else if(json[pos] == _T(']'))
			{
				// массив закончился, выходим на следующей позиции
				pos++; pos_str++;
				return true;
			}
			else 
			{
				RETURN_ERROR(APL_T("Ожидается разделитель элементов массива ',' или его окончание ']'."));
			}
		}
	}
	else if(json[pos] == _T('"'))
	{
		// строка
		pos++;pos_str++; // внутрь надо войти на позиции, следующей после "
		str = GetJsonString(json, pos, err_str, line, pos_str);
		Set(str);
	}
	else if(json[pos] == _T(','))
	{
		// уже разделитель значений, а значения все нет
		RETURN_ERROR(APL_T("Перед символом ',' должно быть задано значение."));
	}
	else if(json[pos] == _T(']'))
	{
		// уже разделитель значений, а значения все нет
		//RETURN_ERROR(APL_T("Перед символом ']' должно быть задано значение."));
		SetNull(); // в json может быть пустой массив
	}
	else
	{
		// число целое, число дробное, булевое, null
		// выделяем лексему из цифр, английских букв и точки
		str = _T("");
		bool flag_stop = false;
		bool flag_point = false;
        while(pos < (int)json.GetLength())
		{
			if(json[pos] ==  _T('\n'))
			{
				line++;pos_str=-1;pos++;
				flag_stop=true;
			}
			else if(json[pos] == _T(' ') ||
					json[pos] == _T('\r') ||
					json[pos] == _T('\t'))
			{
				flag_stop=true;
			}
			else
			{
				if(json[pos] == _T('.'))
					flag_point = true;

				if((json[pos] >= _T('a') && json[pos] <=_T('z')) ||
					 (json[pos] >= _T('A') && json[pos] <=_T('Z')) ||
					 (json[pos] >= _T('а') && json[pos] <=_T('я')) ||
					 (json[pos] >= _T('А') && json[pos] <=_T('Я')) ||
					 (json[pos] >= _T('0') && json[pos] <=_T('9')) ||
					 json[pos] == _T('.') || json[pos] == _T('_')  || 
					 json[pos] == _T('-') || 
					 (pos>0 && (json[pos] == _T('+') || json[pos] == _T('-')) && (json[pos-1] == 'E' || json[pos-1] == 'e')) )
				{
					str+=json[pos];
					pos++;
					pos_str++;
				}
				else
					flag_stop=true;

			}

			if(flag_stop)
			{
				break;
			}
		}
		str.MakeLower();
		if(str == _T("null"))
		{
			SetNull();
		}
		else if(str == _T("true"))
		{
			bool bval = true;
			Set(bval);
		}
		else if(str == _T("false"))
		{
			bool bval = false;
			Set(bval);
		}
		else if(flag_point)
		{
			double r_val = __atof(str);
			if(r_val == 0 && str != _T("0") && str != _T("0.0"))
			{
				// проверяем, вдруг там 00.0 или 00000.000
				str.Trim();
				if(str.GetLength() == 0)
					RETURN_ERROR(APL_T("Некорректное значение."));
                for(int indx=0; indx < (int)str.GetLength(); indx++)
				{
					if(str[indx] != _T('0') && str[indx] != _T('.'))
						RETURN_ERROR(APL_T("Некорректное значение '") + str + _T("'."));
				}
			}

			Set(r_val);
		}
		else if(str.IsEmpty())
		{
			SetNull();
		}
		else
		{
			int i_val = _atoi(str);
			if(i_val == 0 && str != _T("0"))
			{
				// проверяем, вдруг там 00 или 00000000
				str.Trim();
				if(str.GetLength() == 0)
					RETURN_ERROR(APL_T("Некорректное значение."));
                for(int indx=0; indx < (int)str.GetLength(); indx++)
				{
					if(str[indx] != _T('0'))
						RETURN_ERROR(APL_T("Некорректное значение '") + str + _T("'."));
				}
			}
			Set(i_val);
		}
	}

	return true;
}


bool SJsonEl::ParseText2Json(const CString &json, CString &err_str)
{
	Clear();
	err_str = _T("");
	if(json == _T("")) return false;
	int pos = 0;
	int line = 1, pos_str = 0;
	if(!ScrollSpace(json, pos, line, pos_str))
	{
		RETURN_ERROR(APL_T("Некорректное завершение строки JSON. Ожидается начало первого элемента."));
	}
	if(json[pos] != _T('{'))
	{
		RETURN_ERROR(APL_T("Ожидается начало первого элемента '{'."));
	}

	bool ret_val = ParseText2JsonInternal(json, pos, err_str, line, pos_str) /*|| err_str != _T("")*/;
	if(!ret_val) return false;
	ScrollSpace(json, pos, line, pos_str);
    if(pos < (int)json.GetLength())
		RETURN_ERROR(APL_T("Некорректное окончания JSON: после завершения корневого элемента в строке есть другие данные."));

	return true;
}

// Чтение инстанса из объекта или по индексу
CaplInstance* SJsonEl::ReadAttrInst(CaplStepData *data, SJsonVal *p_val, CaplMap & map_index2inst, bool is_putattr, CString & err_str, 
									aplExtent *ext_loaded, aplExtent *ext_change, aplExtent *ext_4del)
{
	CaplInstance* inst_in = 0;
	if(p_val==0) return 0;
	if(p_val->GetType() == SJT_NULL)
		return 0;
	else if(p_val->GetType() == SJT_INT)
	{
		// Это индекс в массиве 
		int index_attr;
		p_val->Get(index_attr);
		if(index_attr<0)
			RETURN_ERROR_I(p_val, APL_T("Индекс инстанса должен быть задан неотрицательным числом"));

		inst_in = (CaplInstance*)map_index2inst.GetByInP(index_attr);
		if(inst_in == 0)
		{
			// Создаем пустышку и заносим в мапу
			inst_in = new CaplInstance(data, 0, -1, false);
			map_index2inst.Add(index_attr, inst_in);
			// чтобы память не терялась
			data->instances.Unique=false;
			data->instances.Add(inst_in);
			data->instances.Unique=true;
		}
	}
	else if(p_val->GetType() == SJT_OBJECT)
	{
		SJsonEl *p_el_in;
		p_el_in = p_val->GetEl();
		if(p_el_in==0){ RETURN_ERROR_I(p_val, APL_T("Не задан объект для инстанса"));}
		inst_in = p_el_in->LoadInstFromJsonInternal(data, map_index2inst, is_putattr, err_str, ext_loaded, ext_change, ext_4del);
		if(inst_in == 0)
			return 0;
	}
	else
		RETURN_ERROR_I(p_val, APL_T("Ссылка на инстанс должна быть задана объектом или индексом в массиве"));

	return inst_in;
}

CaplInstance* SJsonEl::LoadInstFromJsonInternal(CaplStepData *data, CaplMap &map_index2inst, bool is_putattr, CString &err_str,
												aplExtent *ext_loaded, aplExtent *ext_change, aplExtent *ext_4del)
{
	SJsonVal *p_val_id, *p_val_tmp, *p_val_index, *p_val_access;
	int id = 0, index = -1;
	CaplInstance *inst = 0;
	CaplEntity *ent = 0;
    CString str, str_type(_T(""));
	aplAccessModeType access = aplNOT_DEFINED;

	///////////////////////////////////////////
	// Идентификаторы - постоянный и временный пользовательский

	p_val_id = GetKeyVal(_T("id"));
	p_val_tmp = GetKeyVal(_T("id_tmp"));
	p_val_index = GetKeyVal(_T("index"));
	p_val_access = GetKeyVal(_T("access"));

	if(p_val_index!=0 && p_val_tmp != 0)
		RETURN_ERROR_I(p_val_index, 
			APL_T("Нельзя задавать одновременно параметры 'index' и 'id_tmp'. Должен быть определен только один из них, лучше использовать 'index'"));

	if(p_val_index != 0 && p_val_index->GetType() != SJT_INT)
		RETURN_ERROR_I(p_val_index, APL_T("Индекс в массиве 'index' должен быть задан целым числом"));
	if(p_val_tmp != 0 && p_val_tmp->GetType() != SJT_INT)
		RETURN_ERROR_I(p_val_tmp, APL_T("Пользовательский идентификатор нового инстанса 'id_tmp' должен быть задан целым числом"));

	if(p_val_index != 0) p_val_index->Get(index);
	else if(p_val_tmp !=0 ) p_val_tmp->Get(index);

	if(p_val_id!=0)p_val_id->Get(id);

	///////////////////////////////////////////
	// Тип инстанса

	SJsonVal *p_val_type = GetKeyVal(_T("type"));
	if(p_val_type == 0 || p_val_type->GetType() == SJT_NULL)
	{
		// ничего не делаем, ent и так равен нулю
	}
	else if(p_val_type->GetType() == SJT_STRING)
	{
        p_val_type->Get(str_type);
        ent = data->GetEntityBN(str_type);
        if(ent == 0) RETURN_ERROR_I(p_val_type, APL_T("В словаре не найден тип инстанса '%s'"), (LPCTSTR)str_type);
	}
	else
	{
		RETURN_ERROR_I(p_val_type, APL_T("Тип инстанса 'type' должен быть задан строкой или значением null"));
	}
	///////////////////////////////////////////
	// доступ
	if(p_val_access !=0 )
	{
		p_val_access->Get(str);
		if(str==_T("OWN")){access=aplOWN;}
		else if(str==_T("RW")){access=aplRW;}
		else if(str==_T("RO")){access=aplRO;}
		else if(str==_T("NA")){access=aplNO;}
		else RETURN_ERROR_I(p_val_access, APL_T("Уровень доступа задан некорректным значением '%s'"), (LPCTSTR)str);
	}

	///////////////////////////////////////////
	// читаем объекты инстансов

	if(id == 0)
	{
		// Или новый инстанс в REST сохранении от клиента
		// Или Json из файла с отсутствующими id
		// В обоих случаях должен быть задан (index||id_imp) и тип
		if(index==-1)
			RETURN_ERROR_I_THIS(
				APL_T("Для инстанса без идентификатора (в т.ч. для нового инстанса) должен быть задан индекс в массиве 'index' (для старого кода - пользовательский идентификатор 'id_tmp')"));
		if(ent == 0)
			RETURN_ERROR_I_THIS(APL_T("Для инстанса без идентификатора (нового инстанса) должен быть задан тип"));

		inst = (CaplInstance*)map_index2inst.GetByInP(index);
		if(inst == 0)
		{
			inst = data->CreateInstance(ent);
			map_index2inst.Add(index, inst); // У новых инстансов id==0 и задан тип
			if(access!=aplNOT_DEFINED) inst->SetAccessmode(access);
		}
		else if(inst->GetId()==(apl_id)-1)
		{
			// Новый инстанс, уже встречался в атрибутах других инстансов. Перезадаем его параметры
			inst->SetId(0);
			inst->SetServiceParamFull(ent);
			if(access!=aplNOT_DEFINED) inst->SetAccessmode(access);
		}
		else if(inst->GetType() != ent)
		{
			RETURN_ERROR_I_THIS(
                APL_T("Для инстанса без идентификатора (нового инстанса) с индексом %i в разных местах указан разный тип ('%s'и '%s')"),
					index, LPCTSTR(ent->name), LPCTSTR(inst->GetType()->name));
		}
	}
	else
	{
		inst = data->GetInstById((apl_id) id);
		if(inst == 0)
		{
            if(ent==0 && ExtCreateInstance==0)
                RETURN_ERROR_I_THIS(APL_T("Инстанс #%u не найден в БД и для него не указан тип. Невозможно обработать такой инстанс."), id);

			if(index != -1)
			{
				// Скорее всего, чтение из файла, в котором заданы id инстансов. Возможно, дочитка. Или
				//	в браузере при работе с rest проиндексировали все инстансы, а не только новые
				inst = (CaplInstance*)map_index2inst.GetByInP(index);
				if(inst != 0)
				{
					if(inst->GetId()==(apl_id)-1)
					{
						// Инстанс встречался только в атрибутах других инстансов. Перезадаем его параметры
						if(ExtCreateInstance != 0)
							ExtCreateInstance(data, ent, (apl_id) id, inst); // Если сервер PG-ORA, то вызовем его функцию
						else
						{
							inst->SetId(id);
							inst->SetServiceParamFull(ent);
							if(access!=aplNOT_DEFINED) inst->SetAccessmode(access);
						}
					}
					// else // Ситуация штатная. Инстансы можно описывать внутри атрибутов ссылок, поэтому для одного инстанса может быть несколько описаний
				}
				else
				{
					// В кеше и в мапе его нет. Создаем и добавляем
					if(ExtCreateInstance != 0)
						inst = ExtCreateInstance(data, ent, (apl_id) id, 0);
					else
					{
						inst = data->CreateInstance(ent);
						inst->SetId(id);
						if(access!=aplNOT_DEFINED) inst->SetAccessmode(access);
					}
					map_index2inst.Add(index, inst);
				}
			}
			else
			{
				// Старый инстанс по схеме REST, ссылки на атрибуты передаются вложенными объектами, индексы только у новых инстансов
				// Для сервера ora-pg ситуация нормальная, инстанс где-то в недрах БД и в текущий кеш еще не прочитан. 
				if(ExtCreateInstance != 0)
					inst = ExtCreateInstance(data, ent, (apl_id) id, 0);
				else
				{
					// Для Lite сервера ситуация ошибочная - все старые инстансы должны находиться поиском GetInstById 
					RETURN_ERROR_I_THIS(APL_T("В файле/БД не найден инстанс с идентификатором %i"), id);
				}
			}
		}
        else
		{
            ent = inst->GetType();
            if(!str_type.IsEmpty() && ent!=0 && ent->name != str_type)
                RETURN_ERROR_I_THIS(APL_T("У инстанса #%u не совпадает тип в БД '%s' и тип в json '%s'."),
                                    id, (LPCTSTR)ent->name, (LPCTSTR)str_type);

			if(index!=-1)
				map_index2inst.Add(index, inst);
		}
	}

	if(inst == 0 ) RETURN_ERROR_I_THIS(APL_T("Не удалось найти или создать инстанс c id %i, index %i"), id, index);

	///////////////////////////////////////////
	// узел с атрибутами
	SJsonVal *p_val_attrs = GetKeyVal(_T("attributes"));

	///////////////////////////////////////////
    if(ent == 0 && p_val_type != 0)
	{
		// заносим в список на удаление
		if(ext_4del!=0)ext_4del->Add(inst);
		if(p_val_attrs !=0 && p_val_attrs->GetType() != SJT_NOTYPE) RETURN_ERROR_I(p_val_attrs, APL_T("Для удаляемого инстанса нельзя задавать атрибуты"));

	}
	else if(ext_loaded!=0)
	{
		// заносим в список прочитанных
		ext_loaded->Add(inst);
	}

	///////////////////////////////////////////
	// читаем атрибуты

	SJsonEl *p_el_attrs;
	CaplInstance* inst_in;
	CString key;
	CaplAttr *attr;
	int i_val;
	bool b_val;
	double r_val;
	CaplAggr aggr;

	if(p_val_attrs ==0 || p_val_attrs->GetType() == SJT_NOTYPE)
	{
		// атрибуты не заданы
		return inst;
	}
	p_el_attrs = p_val_attrs->GetEl();
	if(p_el_attrs ==0) RETURN_ERROR_I(p_val_attrs, APL_T("Атрибуты инстанса должны быть заданы объектом"));

	if(ext_change!=0)
		ext_change->Add(inst);


	for(int i=0; i<p_el_attrs->key_vals.GetSize(); i++) //!!!
	{
		key = p_el_attrs->key_vals.GetAt(i)->str;
		SJsonVal* p_val = (SJsonVal*)p_el_attrs->key_vals.GetAt(i)->val;
		if(p_val==0) RETURN_ERROR_I(p_el_attrs, APL_T("Не задано значение атрибута '%s'"), (LPCTSTR)key);

		attr = data->GetAttrDefinition(ent, key);
		if(attr == 0) RETURN_ERROR_I(p_val, APL_T("У инстанса %i ('%s') отсутствует атрибут '%s'"),
														inst->GetId(), LPCTSTR(ent->name), LPCTSTR(key));

		///////////////////////////////////////////
		// значение атрибута

		if(attr->type == aplSELECT || attr->type == aplINSTANCE)
		{
			inst_in = ReadAttrInst(data, p_val, map_index2inst, is_putattr, err_str, ext_loaded, ext_change, ext_4del);
			if(inst_in==0)
			{
				if(err_str.IsEmpty()) continue;
				return 0;
			}
			if(is_putattr)
				data->PutAttr(inst, attr, inst_in);

			continue;
		}

		if(attr->type == aplAGGR)
		{
			if(p_val->GetType() != SJT_ARRAY)
				RETURN_ERROR_I(p_val, APL_T("У инстанса %i ('%s') атрибут '%s' должен быть задан массивом"),
														inst->GetId(), LPCTSTR(ent->name), LPCTSTR(key));

			aggr.Clear();
			aplValueType aggr_type = attr->add_types[0];
			
			if(!is_putattr && aggr_type != aplSELECT && aggr_type != aplINSTANCE)
				continue;

			for(int j=0; j<p_val->GetArraySize(); j++)
			{
				SJsonVal *p_val_arr = p_val->GetArrayEl(j);
				if(p_val_arr==0) continue;
				if(p_val_arr->GetType()==SJT_NOTYPE || p_val_arr->GetType()==SJT_NULL) continue;

				if(aggr_type == aplSELECT || aggr_type == aplINSTANCE)
				{
					inst_in = ReadAttrInst(data, p_val_arr, map_index2inst, is_putattr, err_str, ext_loaded, ext_change, ext_4del);
					if(inst_in == 0)
					{
						if(err_str.IsEmpty()) continue;
						return 0;
					}
					if(is_putattr)
						aggr.Add(inst_in);
				}
				else if(aggr_type == aplENUMERATION || aggr_type == aplSTRING)
				{
					if(p_val_arr->GetType() != SJT_STRING)
						RETURN_ERROR_I(p_val_arr, APL_T("У инстанса %i ('%s') атрибут '%s' должен быть задан массивом строк"), 
									inst->GetId(), LPCTSTR(ent->name), LPCTSTR(key));

					p_val_arr->Get(str);
					aggr.Add(str);
				}
				else if(aggr_type == aplINTEGER)
				{
					if(p_val_arr->GetType() != SJT_INT)
						RETURN_ERROR_I(p_val_arr, APL_T("У инстанса %i ('%s') атрибут '%s' должен быть задан массивом целых чисел"), 
											inst->GetId(), LPCTSTR(ent->name), LPCTSTR(key));

					p_val_arr->Get(i_val);
					aggr.Add(i_val);
				}
				else if(aggr_type == aplREAL)
				{
					if(p_val_arr->GetType() != SJT_REAL)
						RETURN_ERROR_I(p_val_arr, APL_T("У инстанса %i ('%s') атрибут '%s' должен быть задан массивом дробных чисел"),
											inst->GetId(), LPCTSTR(ent->name), LPCTSTR(key));

					p_val_arr->Get(r_val);
					aggr.Add(r_val);
				}
				else
				{
					RETURN_ERROR_I(p_val_arr, APL_T("У инстанса %i ('%s') тип атрибута '%s' массив массивов. Такой тип не поддерживается."), 
											inst->GetId(), LPCTSTR(ent->name), LPCTSTR(key));
				}
			}
			if(is_putattr)
				data->PutAttr(inst, attr, aggr);
			continue;
		}

		if(!is_putattr)
			continue;

		if(attr->type == aplENUMERATION || attr->type == aplSTRING)
		{
			if(p_val->GetType() != SJT_STRING)
				RETURN_ERROR_I(p_val, APL_T("У инстанса %i ('%s') атрибут '%s' должен быть задан строкой"), 
												inst->GetId(), LPCTSTR(ent->name), LPCTSTR(key));

			p_val->Get(str);
			data->PutAttr(inst, attr, str);
		}
		else if(attr->type == aplINTEGER)
		{
			if(p_val->GetType() != SJT_INT)
			RETURN_ERROR_I(p_val, APL_T("У инстанса %i ('%s') атрибут '%s' должен быть задан целым числом"), 
											inst->GetId(), LPCTSTR(ent->name), LPCTSTR(key));

			p_val->Get(i_val);
			data->PutAttr(inst, attr, i_val);
		}
		else if(attr->type == aplREAL)
		{
			if(p_val->GetType() == SJT_REAL){p_val->Get(r_val);data->PutAttr(inst, attr, r_val);}
			else if(p_val->GetType() == SJT_INT){p_val->Get(i_val);data->PutAttr(inst, attr, i_val);}
			else
				RETURN_ERROR_I(p_val, APL_T("У инстанса %i ('%s') атрибут '%s' должен быть задан дробным числом"), 
											inst->GetId(), LPCTSTR(ent->name), LPCTSTR(key));
		}
		else if(attr->type == aplBOOL || attr->type == aplLOGICAL)
		{
			if(p_val->GetType() != SJT_BOOL)
				RETURN_ERROR_I(p_val, APL_T("У инстанса %i ('%s') атрибут '%s' должен быть задан булевым значением"), 
											inst->GetId(), LPCTSTR(ent->name), LPCTSTR(key));

			p_val->Get(b_val);
			data->PutAttr(inst, attr, b_val);
		}
		else if(attr->type == aplBINARY || attr->type == aplNOTYPE)
		{
			RETURN_ERROR_I(p_val, APL_T("Атрибуты типов aplBINARY и aplNOTYPE не передаются в виде JSON. Инстанс %i ('%s') атрибут '%s' "), 
											inst->GetId(), LPCTSTR(ent->name), LPCTSTR(key));
		}
// 		else
// 		{
// 
// 		}
	}
	
	return inst;
}

bool SJsonEl::LoadInstancesFromJson(CaplStepData *data, CaplMap &map_index2inst, CString &err_str, 
									aplExtent *ext_loaded, aplExtent *ext_change, aplExtent *ext_4del)
{
	err_str = _T("");
	map_index2inst.RemoveAll();
	map_index2inst.bAutoSort = true;
	if(ext_loaded!=0)ext_loaded->Clear();
	if(ext_change!=0)ext_change->Clear();
	if(ext_4del!=0)ext_4del->Clear();

	SJsonVal* p_json_arr_instances;
	CString str = _T("");
	int i;

	if(data == 0){ err_str = APL_T("В функцию передан пустой объект CaplStepData!"); return false;}

	// Проверка версии буфера и словаря
	GetKeyVal(_T("format"), str);
	if(str != _T("apl_json_1"))
	{
		err_str.Format( APL_T("У блока json неизвестный формат буфера '%s'. Поддерживается формат: 'apl_json_1'"), LPCTSTR(str));
		return false;
	}

	SJsonVal* jv = GetKeyVal(_T("dictionary"));
	if(jv==0)
	{
		err_str = APL_T("В json не задан словарь.");
		return false;
	}
	if(jv->GetType()==SJT_STRING)
	{
		jv->Get(str);
		if(data->m_CurSchema.IsEmpty())
		{
			if(!data->LoadDictionary(str)){err_str = data->GetLastErrorDescription(); return false; }
		}
		else if(str != data->m_CurSchema)
		{
			err_str.Format(APL_T("Словарь блока json '%s' не совпадает с текущим словарем '%s'"), LPCTSTR(str), LPCTSTR(data->m_CurSchema));
			return false;
		}
	}
	else if(jv->GetType()==SJT_ARRAY)
	{
		CaplStrMap dicts;
		for(int i=0; i<jv->GetArraySize();i++)
		{
			SJsonEl *el= jv->GetArrayEll(i);
			if(el==0) continue;
			CStringArray keys; el->GetAllKeys(keys);
			if(keys.GetSize()==0) continue;
			int ver=0; 
			el->GetKeyVal(keys.GetAt(0), ver);
			dicts.Add(keys[0], ver);
		}
		if(!data->LoadDictionaryAddition(dicts)){err_str = data->GetLastErrorDescription(); return false; }
	}

	p_json_arr_instances = GetKeyVal(_T("instances"));
	if(p_json_arr_instances == 0 || p_json_arr_instances->GetType() == SJT_NOTYPE)
	{
		err_str.Format(APL_T("В буфере JSON отсутствует массив instances"));
		return false;
	}
	if(p_json_arr_instances->GetArraySize() == 0)
	{
		PRINT_ERROR_I(p_json_arr_instances, APL_T("В буфере JSON пустой массив instances")); 
		return false;
	}

	SJsonVal *p_val;
	SJsonEl *p_el;
	for(i=0; i<p_json_arr_instances->GetArraySize(); i++)
	{
		p_val = p_json_arr_instances->GetArrayEl(i);
		if(p_val==0){ PRINT_ERROR_I(p_json_arr_instances, APL_T("Ожидается объект с описанием инстанса")); return false; }
		p_el = p_val->GetEl();
		if(p_el==0){ PRINT_ERROR_I(p_val, APL_T("Ожидается объект с описанием инстанса")); return false; }

		if(p_el->LoadInstFromJsonInternal(data, map_index2inst, false, err_str, ext_loaded, ext_change, ext_4del) == 0)
		{
			return false;
		}
	}
	for(i=0; i<map_index2inst.GetSize(); i++)
	{
		CaplInstance* inst = (CaplInstance*)map_index2inst.Data[i].out;
		if(inst==0){PRINT_ERROR_I_THIS(APL_T("Не задан объект для индекса %i"), map_index2inst.Data[i].in); return false;}
		if(inst->GetId()==(apl_id)-1){ PRINT_ERROR_I_THIS(APL_T("Не задан объект для индекса %i"), map_index2inst.Data[i].in); return false;}
	}

    if(ExtGetType != 0 && ext_loaded!=0)
    {
        CString list_inst = _T("");
        if(!ExtGetType(data, *ext_loaded, list_inst))
        {
            err_str = APL_T("В JSON некорректные инстансы: ") + list_inst;
            return false;
        }
    }

	for(i=0; i<p_json_arr_instances->GetArraySize(); i++)
	{
		p_val = p_json_arr_instances->GetArrayEl(i);
		if(p_val==0){ PRINT_ERROR_I(p_json_arr_instances, APL_T("Ожидается объект с описанием инстанса")); return false; }
		p_el = p_val->GetEl();
		if(p_el==0){ PRINT_ERROR_I(p_val, APL_T("Ожидается объект с описанием инстанса")); return false; }

		if(p_el->LoadInstFromJsonInternal(data, map_index2inst, true, err_str, ext_loaded, ext_change, ext_4del) == 0)
		{
			return false;
		}
	}

	return true;
}


bool SJsonEl::SaveExtent2Json(CaplStepData *data, aplExtent &ext, bool save_temporary)
{
	int i,instmapsize;
	CString buf,buf1;

	Clear();
	if(data==0){m_ErrDescr=APL_T("Пустой объект data"); return false;}

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

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

	for(i=0;i<instmapsize;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

		AddInst2JsonRoot(inst_i, 0, aplNOT_DEFINED, instmap, instmapsize);
	}

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

	return true;
}
