using FastReport.Utils.Json;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
namespace FastReport
{
///
/// Allows working with JsonObject
///
public abstract class JsonBase
{
#region Private Fields
private static readonly NumberFormatInfo format;
#endregion Private Fields
#region Public Indexers
///
/// Returns child object for JsonArray
///
///
///
public virtual object this[int index]
{
get
{
return null;
}
set
{
}
}
///
/// Returns child object for JsonObject
///
///
///
public virtual object this[string key]
{
get
{
return null;
}
set
{
}
}
#endregion Public Indexers
#region Public Properties
///
/// Returns count of child object
///
public virtual int Count
{
get
{
return 0;
}
}
///
/// Returns true if this object is JsonArray
///
public virtual bool IsArray
{
get
{
return false;
}
}
///
/// Returns true if this object is JsonObject
///
public virtual bool IsObject
{
get
{
return false;
}
}
///
/// Returns list of JsonObject keys
///
public virtual IEnumerable Keys
{
get
{
yield break;
}
}
#endregion Public Properties
#region Public Constructors
static JsonBase()
{
format = new NumberFormatInfo();
format.NumberGroupSeparator = String.Empty;
format.NumberDecimalSeparator = ".";
}
#endregion Public Constructors
#region Public Methods
///
/// Pars json text string and return a new JsonBase Object
///
///
///
public static JsonBase FromString(string jsonText)
{
using (JsonTextReader reader = new JsonTextReader(jsonText))
{
return FromTextReader(reader);
}
}
///
/// returns true
///
///
///
public virtual bool ContainsKey(string key)
{
return false;
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
WriteTo(sb, 0);
return sb.ToString();
}
///
/// Serialize this object to sb
///
///
///
/// indent in space, 0 = without indent
public abstract void WriteTo(StringBuilder sb, int indent);
#endregion Public Methods
#region Internal Methods
internal string ReadString(string key)
{
object result = this[key];
if (result != null)
{
return result.ToString();
}
return null;
}
internal void WriteValue(StringBuilder sb, object item, int indent)
{
if (item is JsonBase jsonBase)
{
if (indent > 0)
{
jsonBase.WriteTo(sb, indent + 2);
}
else
{
jsonBase.WriteTo(sb, 0);
}
}
else if (item is bool)
{
if (item.Equals(true))
{
sb.Append("true");
}
else
{
sb.Append("false");
}
}
else if (IsNumber(item))
{
sb.Append(((IConvertible)item).ToString(format));
}
else if (item == null)
{
sb.Append("null");
}
else
{
sb.Append('"');
foreach (char c in item.ToString())
{
switch (c)
{
case '"': sb.Append("\\\""); break;
case '\\': sb.Append("\\\\"); break;
case '/': sb.Append("\\/"); break;
case '\b': sb.Append("\\b"); break;
case '\f': sb.Append("\\f"); break;
case '\n': sb.Append("\\n"); break;
case '\r': sb.Append("\\r"); break;
case '\t': sb.Append("\\t"); break;
default: sb.Append(c); break;
}
}
sb.Append('"');
}
}
#endregion Internal Methods
#region Private Methods
private static JsonBase FromTextReader(JsonTextReader reader)
{
reader.SkipWhiteSpace();
if (reader.IsNotEOF)
switch (reader.Char)
{
case '{':
return ReadObject(reader);
case '[':
return ReadArray(reader);
default:
throw reader.ThrowFormat('{', '[');
}
throw reader.ThrowEOF('{', '[');
}
private static JsonArray ReadArray(JsonTextReader reader)
{
if (reader.Char != '[')
throw reader.ThrowFormat('[');
JsonArray result = new JsonArray();
reader.ReadNext();
reader.SkipWhiteSpace();
if (reader.IsEOF)
throw reader.ThrowEOF(']');
else if (reader.Char != ']')
{
while (true)
{
result.Add(ReadValue(reader));
reader.SkipWhiteSpace();
if (reader.IsEOF)
throw reader.ThrowEOF(']');
else if (reader.Char == ',')
{
reader.ReadNext();
reader.SkipWhiteSpace();
continue;
}
else if (reader.Char == ']')
break;
else reader.ThrowFormat(',', ']');
}
}
reader.ReadNext();
return result;
}
private static JsonObject ReadObject(JsonTextReader reader)
{
if (reader.Char != '{')
throw reader.ThrowFormat('{');
JsonObject result = new JsonObject();
reader.ReadNext();
reader.SkipWhiteSpace();
if (reader.IsEOF)
throw reader.ThrowEOF('}');
else if (reader.Char != '}')
{
while (true)
{
string key = reader.Dedublicate(ReadValueString(reader));
reader.SkipWhiteSpace();
if (reader.IsEOF)
throw reader.ThrowEOF(':');
else if (reader.Char != ':')
reader.ThrowFormat(':');
reader.ReadNext();
reader.SkipWhiteSpace();
result[key] = ReadValue(reader);
reader.SkipWhiteSpace();
if (reader.IsEOF)
throw reader.ThrowEOF('}');
else if (reader.Char == ',')
{
reader.ReadNext();
reader.SkipWhiteSpace();
continue;
}
else if (reader.Char == '}')
break;
else reader.ThrowFormat(',', '}');
}
}
reader.ReadNext();
return result;
}
private static object ReadValue(JsonTextReader reader)
{
if (reader.IsEOF)
throw reader.ThrowEOF('"', '[', '{', 'n', 't', 'f', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9');
switch (reader.Char)
{
case '"':
return ReadValueString(reader);
case '[':
return ReadArray(reader);
case '{':
return ReadObject(reader);
case 'n':
return ReadValue(reader, "null", null);
case 't':
return ReadValue(reader, "true", true);
case 'f':
return ReadValue(reader, "false", false);
case '-':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
return ReadValueNumber(reader);
default:
throw reader.ThrowFormat('"', '[', '{', 'n', 't', 'f', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9');
}
}
private static object ReadValue(JsonTextReader reader, string str, object result)
{
for (int i = 0; i < str.Length; i++)
{
if (reader.IsEOF)
throw reader.ThrowEOF(str[i]);
else if (reader.Char != str[i])
throw reader.ThrowFormat(str[i]);
reader.ReadNext();
}
return result;
}
private static double ReadValueNumber(JsonTextReader reader)
{
int startPos = reader.Position;
if (reader.IsEOF)
throw reader.ThrowEOF('-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9');
if (reader.Char == '-')
{
reader.ReadNext();
if (reader.IsEOF)
throw reader.ThrowEOF('0', '1', '2', '3', '4', '5', '6', '7', '8', '9');
}
if (reader.Char < '0' || '9' < reader.Char)
throw reader.ThrowFormat('0', '1', '2', '3', '4', '5', '6', '7', '8', '9');
while (true)
{
reader.ReadNext();
if (reader.IsEOF)
throw reader.ThrowEOF('0', '1', '2', '3', '4', '5', '6', '7', '8', '9');
if (reader.Char < '0' || '9' < reader.Char)
break;
}
if (reader.Char == '.')
{
reader.ReadNext();
if (reader.IsEOF)
throw reader.ThrowEOF('0', '1', '2', '3', '4', '5', '6', '7', '8', '9');
if (reader.Char < '0' || '9' < reader.Char)
throw reader.ThrowFormat('0', '1', '2', '3', '4', '5', '6', '7', '8', '9');
while (true)
{
reader.ReadNext();
if (reader.IsEOF)
throw reader.ThrowEOF('0', '1', '2', '3', '4', '5', '6', '7', '8', '9');
if (reader.Char < '0' || '9' < reader.Char)
break;
}
}
if (reader.Char == 'e' || reader.Char == 'E')
{
reader.ReadNext();
if (reader.IsEOF)
throw reader.ThrowEOF('+', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9');
bool signed = false;
if (reader.Char == '+')
{
reader.ReadNext();
signed = true;
if (reader.IsEOF)
throw reader.ThrowEOF('0', '1', '2', '3', '4', '5', '6', '7', '8', '9');
}
else if (reader.Char == '-')
{
reader.ReadNext();
signed = true;
if (reader.IsEOF)
throw reader.ThrowEOF('0', '1', '2', '3', '4', '5', '6', '7', '8', '9');
}
if (reader.Char < '0' || '9' < reader.Char)
{
if (signed)
throw reader.ThrowFormat('0', '1', '2', '3', '4', '5', '6', '7', '8', '9');
else
throw reader.ThrowFormat('-', '+', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9');
}
while (true)
{
reader.ReadNext();
if (reader.IsEOF)
throw reader.ThrowEOF('0', '1', '2', '3', '4', '5', '6', '7', '8', '9');
if (reader.Char < '0' || '9' < reader.Char)
break;
}
}
string value = reader.Substring(startPos, reader.Position - startPos);
return double.Parse(value, format);
}
private static string ReadValueString(JsonTextReader reader)
{
if (reader.IsEOF)
throw reader.ThrowEOF('"');
else if (reader.Char != '"')
throw reader.ThrowFormat('"');
reader.ReadNext();
StringBuilder sb = new StringBuilder();
while (true)
{
if (reader.IsEOF)
throw reader.ThrowEOF('"');
if (reader.Char == '"')
break;
else if (reader.Char == '\\')
{
reader.ReadNext();
if (reader.IsEOF)
throw reader.ThrowEOF('"', '\\', '/', 'b', 'f', 'n', 'r', 't', 'u');
switch (reader.Char)
{
case '"': sb.Append('"'); break;
case '\\': sb.Append('\\'); break;
case '/': sb.Append('/'); break;
case 'b': sb.Append('\b'); break;
case 'f': sb.Append('\f'); break;
case 'n': sb.Append('\n'); break;
case 'r': sb.Append('\r'); break;
case 't': sb.Append('\t'); break;
case 'u':
int number = 0;
for (int i = 0; i < 4; i++)
{
reader.ReadNext();
if (reader.IsEOF)
throw reader.ThrowEOF('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'A', 'B', 'C', 'D', 'E', 'F');
if ('0' <= reader.Char && reader.Char <= '9')
{
number = number * 0x10 + (int)(reader.Char - '0');
}
else if ('a' <= reader.Char && reader.Char <= 'f')
{
number = number * 0x10 + 10 + (int)(reader.Char - 'a');
}
else if ('A' <= reader.Char && reader.Char <= 'F')
{
number = number * 0x10 + 10 + (int)(reader.Char - 'A');
}
else
{
throw reader.ThrowFormat('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'A', 'B', 'C', 'D', 'E', 'F');
}
}
sb.Append((char)number);
break;
default:
throw reader.ThrowFormat('"', '\\', '/', 'b', 'f', 'n', 'r', 't', 'u');
}
}
else
{
sb.Append(reader.Char);
}
reader.ReadNext();
}
reader.ReadNext();
return sb.ToString();
}
private static bool IsNumber(object item)
{
return item is float
|| item is double
|| item is sbyte
|| item is byte
|| item is short
|| item is ushort
|| item is int
|| item is uint
|| item is long
|| item is ulong
|| item is decimal;
}
#endregion Private Methods
}
}