Files
2019-04-22 17:32:13 +03:00

931 lines
43 KiB
Plaintext

//+------------------------------------------------------------------ß
//| JAson |
//| This software is licensed under the MIT https://goo.gl/eyJgHe |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2006-2017"
#property version "1.12"
#property strict
//------------------------------------------------------------------ enum enJAType
enum enJAType
{
jtUNDEF, jtNULL, jtBOOL, jtINT, jtDBL, jtSTR, jtARRAY, jtOBJ
};
//------------------------------------------------------------------ class CJAVal
class CJAVal
{
public:
virtual void Clear (enJAType jt = jtUNDEF, bool savekey = false)
{
m_parent = NULL;
if (!savekey)
groupName = "";
m_type = jt;
m_bv = false;
m_iv = 0;
m_dv = 0;
m_prec = 5;
m_sv = "";
ArrayResize (group, 0, 100);
}
virtual bool Copy (const CJAVal &a)
{
groupName = a.groupName;
CopyData (a);
return true;
}
virtual void CopyData (const CJAVal &a)
{
m_type = a.m_type;
m_bv = a.m_bv;
m_iv = a.m_iv;
m_dv = a.m_dv;
m_prec = a.m_prec;
m_sv = a.m_sv;
CopyArr (a);
}
virtual void CopyArr (const CJAVal &a)
{
int n = ArrayResize (group, ArraySize (a.group));
for (int i = 0; i < n; i++)
{
group [i] = a.group [i];
group [i].m_parent = GetPointer (this);
}
}
public:
CJAVal group []; // group
string groupName; // group name
string m_lkey;
CJAVal *m_parent;
enJAType m_type;
bool m_bv;
long m_iv;
double m_dv;
int m_prec;
string m_sv;
static int code_page;
public:
CJAVal ()
{
Clear ();
}
CJAVal (CJAVal *aparent, enJAType atype)
{
Clear ();
m_type = atype;
m_parent = aparent;
}
CJAVal (enJAType t, string a)
{
Clear ();
FromStr (t, a);
}
CJAVal (const int a)
{
Clear ();
m_type = jtINT;
m_iv = a;
m_dv = (double)m_iv;
m_sv = IntegerToString (m_iv);
m_bv = m_iv != 0;
}
CJAVal (const long a)
{
Clear ();
m_type = jtINT;
m_iv = a;
m_dv = (double)m_iv;
m_sv = IntegerToString (m_iv);
m_bv = m_iv != 0;
}
CJAVal (const double a, int aprec = -100)
{
Clear ();
m_type = jtDBL;
m_dv = a;
if (aprec > -100)
m_prec = aprec;
m_iv = (long)m_dv;
m_sv = DoubleToString (m_dv, m_prec);
m_bv = m_iv != 0;
}
CJAVal (const bool a)
{
Clear ();
m_type = jtBOOL;
m_bv = a;
m_iv = m_bv;
m_dv = m_bv;
m_sv = IntegerToString (m_iv);
}
CJAVal (const CJAVal &a)
{
Clear ();
Copy (a);
}
~CJAVal ()
{
Clear ();
}
public:
int Size ()
{
return ArraySize (group);
}
virtual bool IsNumeric ()
{
return m_type == jtDBL || m_type == jtINT;
}
virtual CJAVal *FindKey (string akey)
{
for (int i = Size () - 1; i >= 0; --i)
if (group [i].groupName == akey)
return GetPointer (group [i]);
return NULL;
}
virtual CJAVal *HasKey (string akey, enJAType atype = jtUNDEF)
{
CJAVal *e = FindKey (akey);
if (CheckPointer (e) != POINTER_INVALID)
{
if (atype == jtUNDEF || atype == e.m_type)
return GetPointer (e);
}
return NULL;
}
virtual CJAVal *operator [] (string akey);
virtual CJAVal *operator [] (int i);
void operator= (const CJAVal &a)
{
Copy (a);
}
void operator= (const int a)
{
m_type = jtINT;
m_iv = a;
m_dv = (double)m_iv;
m_bv = m_iv != 0;
}
void operator= (const long a)
{
m_type = jtINT;
m_iv = a;
m_dv = (double)m_iv;
m_bv = m_iv != 0;
}
void operator= (const double a)
{
m_type = jtDBL;
m_dv = a;
m_iv = (long)m_dv;
m_bv = m_iv != 0;
}
void operator= (const bool a)
{
m_type = jtBOOL;
m_bv = a;
m_iv = (long)m_bv;
m_dv = (double)m_bv;
}
void operator= (string a)
{
m_type = (a != NULL) ? jtSTR : jtNULL;
m_sv = a;
m_iv = StringToInteger (m_sv);
m_dv = StringToDouble (m_sv);
m_bv = a != NULL;
}
bool operator== (const int a)
{
return m_iv == a;
}
bool operator== (const long a)
{
return m_iv == a;
}
bool operator== (const double a)
{
return m_dv == a;
}
bool operator== (const bool a)
{
return m_bv == a;
}
bool operator== (string a)
{
return m_sv == a;
}
bool operator!= (const int a)
{
return m_iv != a;
}
bool operator!= (const long a)
{
return m_iv != a;
}
bool operator!= (const double a)
{
return m_dv != a;
}
bool operator!= (const bool a)
{
return m_bv != a;
}
bool operator!= (string a)
{
return m_sv != a;
}
long ToInt () const
{
return m_iv;
}
double ToDbl () const
{
return m_dv;
}
bool ToBool () const
{
return m_bv;
}
string ToStr ()
{
return m_sv;
}
virtual void FromStr (enJAType t, string a)
{
m_type = t;
switch (m_type)
{
case jtBOOL:
m_bv = (StringToInteger (a) != 0);
m_iv = (long)m_bv;
m_dv = (double)m_bv;
m_sv = a;
break;
case jtINT:
m_iv = StringToInteger (a);
m_dv = (double)m_iv;
m_sv = a;
m_bv = m_iv != 0;
break;
case jtDBL:
m_dv = StringToDouble (a);
m_iv = (long)m_dv;
m_sv = a;
m_bv = m_iv != 0;
break;
case jtSTR:
m_sv = Unescape (a);
m_type = (m_sv != NULL) ? jtSTR : jtNULL;
m_iv = StringToInteger (m_sv);
m_dv = StringToDouble (m_sv);
m_bv = m_sv != NULL;
break;
}
}
virtual string GetStr (char &js [], int i, int slen)
{
if (slen == 0)
return "";
char cc [];
ArrayCopy (cc, js, 0, i, slen);
return CharArrayToString (cc, 0, WHOLE_ARRAY, CJAVal::code_page);
}
virtual void Set (const CJAVal &a)
{
if (m_type == jtUNDEF)
m_type = jtOBJ;
CopyData (a);
}
virtual void Set (const CJAVal &list []);
virtual CJAVal *Add (const CJAVal &item)
{
if (m_type == jtUNDEF)
m_type = jtARRAY;
/*ASSERT(m_type==jtOBJ || m_type==jtARRAY);*/ return AddBase (item);
} // добавление
virtual CJAVal *Add (const int a)
{
CJAVal item (a);
return Add (item);
}
virtual CJAVal *Add (const long a)
{
CJAVal item (a);
return Add (item);
}
virtual CJAVal *Add (const double a, int aprec = -2)
{
CJAVal item (a, aprec);
return Add (item);
}
virtual CJAVal *Add (const bool a)
{
CJAVal item (a);
return Add (item);
}
virtual CJAVal *Add (string a)
{
CJAVal item (jtSTR, a);
return Add (item);
}
virtual CJAVal *AddBase (const CJAVal &item) // добавление
{
int c = Size ();
ArrayResize (group, c + 1, 100);
group [c] = item;
group [c].m_parent = GetPointer (this);
return GetPointer (group [c]);
}
virtual CJAVal *New ()
{
if (m_type == jtUNDEF)
m_type = jtARRAY;
/*ASSERT(m_type==jtOBJ || m_type==jtARRAY);*/ return NewBase ();
} // добавление
virtual CJAVal *NewBase () // добавление
{
int c = Size ();
ArrayResize (group, c + 1, 100);
return GetPointer (group [c]);
}
virtual string Escape (string a);
virtual string Unescape (string a);
public:
virtual void Serialize (string &js, bool bf = false, bool bcoma = false);
virtual string Serialize ()
{
string js;
Serialize (js);
return js;
}
virtual bool Deserialize (char &js [], int slen, int &i);
virtual bool ExtrStr (char &js [], int slen, int &i);
virtual bool Deserialize (string js, int acp = CP_ACP)
{
int i = 0;
Clear ();
CJAVal::code_page = acp;
char arr [];
int slen = StringToCharArray (js, arr, 0, WHOLE_ARRAY, CJAVal::code_page);
return Deserialize (arr, slen, i);
}
virtual bool Deserialize (char &js [], int acp = CP_ACP)
{
int i = 0;
Clear ();
CJAVal::code_page = acp;
return Deserialize (js, ArraySize (js), i);
}
};
int CJAVal::code_page = CP_ACP;
//------------------------------------------------------------------ operator[]
CJAVal *CJAVal::operator[] (string akey)
{
if (m_type == jtUNDEF)
m_type = jtOBJ;
CJAVal *v = FindKey (akey);
if (v)
return v;
CJAVal b (GetPointer (this), jtUNDEF);
b.groupName = akey;
v = Add (b);
return v;
}
//------------------------------------------------------------------ operator[]
CJAVal *CJAVal::operator[] (int i)
{
if (m_type == jtUNDEF)
m_type = jtARRAY;
while (i >= Size ())
{
CJAVal b (GetPointer (this), jtUNDEF);
if (CheckPointer (Add (b)) == POINTER_INVALID)
return NULL;
}
return GetPointer (group [i]);
}
//------------------------------------------------------------------ Set
void CJAVal::Set (const CJAVal &list [])
{
if (m_type == jtUNDEF)
m_type = jtARRAY;
int n = ArrayResize (group, ArraySize (list), 100);
for (int i = 0; i < n; ++i)
{
group [i] = list [i];
group [i].m_parent = GetPointer (this);
}
}
//------------------------------------------------------------------ Serialize
void CJAVal::Serialize (string &js, bool bkey/*=false*/, bool coma/*=false*/)
{
if (m_type == jtUNDEF)
return;
if (coma)
js += ",";
if (bkey)
js += StringFormat ("\"%s\":", groupName);
int _n = Size ();
switch (m_type)
{
case jtNULL:
js += "null";
break;
case jtBOOL:
js += (m_bv ? "true" : "false");
break;
case jtINT:
js += IntegerToString (m_iv);
break;
case jtDBL:
js += DoubleToString (m_dv, 5);
break;
case jtSTR:
{
string ss = Escape (m_sv);
if (StringLen (ss) > 0)
js += StringFormat ("\"%s\"", ss);
else
js += "null";
}
break;
case jtARRAY:
js += "[";
for (int i = 0; i < _n; i++)
group [i].Serialize (js, false, i > 0);
js += "]";
break;
case jtOBJ:
js += "{";
for (int i = 0; i < _n; i++)
group [i].Serialize (js, true, i > 0);
js += "}";
break;
}
}
//------------------------------------------------------------------ Deserialize
bool CJAVal::Deserialize (char &js [], int slen, int &i)
{
string num = "0123456789+-.eE";
int i0 = i;
for (; i < slen; i++)
{
char c = js [i];
if (c == 0)
break;
switch (c)
{
case '\t':
case '\r':
case '\n':
case ' ': // пропускаем из имени пробелы
i0 = i + 1;
break;
case '[': // начало массива. создаём объекты и забираем из js
{
i0 = i + 1;
if (m_type != jtUNDEF) // если значение уже имеет тип, то это ошибка
{
Print (groupName + " " + string (__LINE__));
return false;
}
m_type = jtARRAY; // задали тип значения
i++;
CJAVal val (GetPointer (this), jtUNDEF);
while (val.Deserialize (js, slen, i))
{
if (val.m_type != jtUNDEF)
Add (val);
if (val.m_type == jtINT || val.m_type == jtDBL || val.m_type == jtARRAY)
i++;
val.Clear ();
val.m_parent = GetPointer (this);
if (js [i] == ']')
break;
i++;
if (i >= slen)
{
Print (groupName + " " + string (__LINE__));
return false;
}
}
if (i >= slen)
return false;
return js [i] == ']' || js [i] == 0;
}
break;
case ']':
if (!m_parent)
return false;
return m_parent.m_type == jtARRAY; // конец массива, текущее значение должны быть массивом
case ':':
{
if (m_lkey == "")
{
Print (groupName + " " + string (__LINE__));
return false;
}
CJAVal val (GetPointer (this), jtUNDEF);
CJAVal *oc = Add (val); // тип объекта пока не определён
oc.groupName = m_lkey;
m_lkey = ""; // задали имя ключа
i++;
if (!oc.Deserialize (js, slen, i))
{
Print (groupName + " " + string (__LINE__));
return false;
}
break;
}
case ',': // разделитель значений // тип значения уже должен быть определён
i0 = i + 1;
if (!m_parent && m_type != jtOBJ)
{
Print (groupName + " " + string (__LINE__));
return false;
}
else
if (m_parent)
{
if (m_parent.m_type != jtARRAY && m_parent.m_type != jtOBJ)
{
Print (groupName + " " + string (__LINE__));
return false;
}
if (m_parent.m_type == jtARRAY && m_type == jtUNDEF)
return true;
}
break;
// примитивы могут быть ТОЛЬКО в массиве / либо самостоятельно
case '{': // начало объекта. создаем объект и забираем его из js
i0 = i + 1;
if (m_type != jtUNDEF) // ошибка типа
{
Print (groupName + " " + string (__LINE__));
return false;
}
m_type = jtOBJ; // задали тип значения
i++;
if (!Deserialize (js, slen, i)) // вытягиваем его
{
Print (groupName + " " + string (__LINE__));
return false;
}
if (i >= slen)
return false;
return js [i] == '}' || js [i] == 0;
break;
case '}':
return m_type == jtOBJ; // конец объекта, текущее значение должно быть объектом
case 't':
case 'T': // начало true
case 'f':
case 'F': // начало false
if (m_type != jtUNDEF) // ошибка типа
{
Print (groupName + " " + string (__LINE__));
return false;
}
m_type = jtBOOL; // задали тип значения
if (i + 3 < slen)
{
if (StringCompare (GetStr (js, i, 4), "true", false) == 0)
{
m_bv = true;
i += 3;
return true;
}
}
if (i + 4 < slen)
{
if (StringCompare (GetStr (js, i, 5), "false", false) == 0)
{
m_bv = false;
i += 4;
return true;
}
}
Print (groupName + " " + string (__LINE__));
return false; // не тот тип или конец строки
break;
case 'n':
case 'N': // начало null
if (m_type != jtUNDEF) // ошибка типа
{
Print (groupName + " " + string (__LINE__));
return false;
}
m_type = jtNULL; // задали тип значения
if (i + 3 < slen)
if (StringCompare (GetStr (js, i, 4), "null", false) == 0)
{
i += 3;
return true;
}
Print (groupName + " " + string (__LINE__));
return false; // не NULL или конец строки
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '-':
case '+':
case '.': // начало числа
{
if (m_type != jtUNDEF) // ошибка типа
{
Print (groupName + " " + string (__LINE__));
return false;
}
bool dbl = false; // задали тип значения
int is = i;
while (js [i] != 0 && i < slen)
{
i++;
if (StringFind (num, GetStr (js, i, 1)) < 0)
break;
if (!dbl)
dbl = (js [i] == '.' || js [i] == 'e' || js [i] == 'E');
}
m_sv = GetStr (js, is, i - is);
if (dbl)
{
m_type = jtDBL;
m_dv = StringToDouble (m_sv);
m_iv = (long)m_dv;
m_bv = m_iv != 0;
}
else // уточнии тип значения
{
m_type = jtINT;
m_iv = StringToInteger (m_sv);
m_dv = (double)m_iv;
m_bv = m_iv != 0;
}
i--;
return true; // отодвинулись на 1 символ назад и вышли
break;
}
case '\"': // начало или конец строки
if (m_type == jtOBJ) // если тип еще неопределён и ключ не задан
{
i++;
int is = i;
if (!ExtrStr (js, slen, i)) // это ключ, идём до конца строки
{
Print (groupName + " " + string (__LINE__));
return false;
}
m_lkey = GetStr (js, is, i - is);
}
else
{
if (m_type != jtUNDEF) // ошибка типа
{
Print (groupName + " " + string (__LINE__));
return false;
}
m_type = jtSTR; // задали тип значения
i++;
int is = i;
if (!ExtrStr (js, slen, i))
{
Print (groupName + " " + string (__LINE__));
return false;
}
FromStr (jtSTR, GetStr (js, is, i - is));
return true;
}
break;
}
}
return true;
}
//------------------------------------------------------------------ ExtrStr
bool CJAVal::ExtrStr (char &js [], int slen, int &i)
{
for (; js [i] != 0 && i < slen; i++)
{
char c = js [i];
if (c == '\"')
break; // конец строки
if (c == '\\' && i + 1 < slen)
{
i++;
c = js [i];
switch (c)
{
case '/':
case '\\':
case '\"':
case 'b':
case 'f':
case 'r':
case 'n':
case 't':
break; // это разрешенные
case 'u': // \uXXXX
{
i++;
for (int j = 0; j < 4 && i < slen && js [i] != 0; j++, i++)
{
if (!((js [i] >= '0' && js [i] <= '9') || (js [i] >= 'A' && js [i] <= 'F') || (js [i] >= 'a' && js [i] <= 'f'))) // не hex
{
Print (groupName + " " + CharToString (js [i]) + " " + string (__LINE__));
return false;
}
}
i--;
break;
}
default:
break; /*{ return false; } // неразрешенный символ с экранированием */
}
}
}
return true;
}
//------------------------------------------------------------------ Escape
string CJAVal::Escape (string a)
{
ushort as [], s [];
int n = StringToShortArray (a, as);
if (ArrayResize (s, 2 * n) != 2 * n)
return NULL;
int j = 0;
for (int i = 0; i < n; i++)
{
switch (as [i])
{
case '\\':
s [j] = '\\';
j++;
s [j] = '\\';
j++;
break;
case '"':
s [j] = '\\';
j++;
s [j] = '"';
j++;
break;
case '/':
s [j] = '\\';
j++;
s [j] = '/';
j++;
break;
case 8:
s [j] = '\\';
j++;
s [j] = 'b';
j++;
break;
case 12:
s [j] = '\\';
j++;
s [j] = 'f';
j++;
break;
case '\n':
s [j] = '\\';
j++;
s [j] = 'n';
j++;
break;
case '\r':
s [j] = '\\';
j++;
s [j] = 'r';
j++;
break;
case '\t':
s [j] = '\\';
j++;
s [j] = 't';
j++;
break;
default:
s [j] = as [i];
j++;
break;
}
}
a = ShortArrayToString (s, 0, j);
return a;
}
//------------------------------------------------------------------ Unescape
string CJAVal::Unescape (string a)
{
ushort as [], s [];
int n = StringToShortArray (a, as);
if (ArrayResize (s, n) != n)
return NULL;
int j = 0, i = 0;
while (i < n)
{
ushort c = as [i];
if (c == '\\' && i < n - 1)
{
switch (as [i + 1])
{
case '\\':
c = '\\';
i++;
break;
case '"':
c = '"';
i++;
break;
case '/':
c = '/';
i++;
break;
case 'b':
c = 8;
/*08='\b'*/;
i++;
break;
case 'f':
c = 12;
/*0c=\f*/ i++;
break;
case 'n':
c = '\n';
i++;
break;
case 'r':
c = '\r';
i++;
break;
case 't':
c = '\t';
i++;
break;
case 'u': // \uXXXX
{
i += 2;
ushort k = 0;
for (int jj = 0; jj < 4 && i < n; jj++, i++)
{
c = as [i];
ushort h = 0;
if (c >= '0' && c <= '9')
h = c - '0';
else
if (c >= 'A' && c <= 'F')
h = c - 'A' + 10;
else
if (c >= 'a' && c <= 'f')
h = c - 'a' + 10;
else
break; // не hex
k += h * (ushort)pow (16, (3 - jj));
}
i--;
c = k;
break;
}
}
}
s [j] = c;
j++;
i++;
}
a = ShortArrayToString (s, 0, j);
return a;
}
//+------------------------------------------------------------------+