using System.Globalization; namespace DTLib.Dtsod; public class DtsodV30 : DtsodDict, IDtsod { public DtsodVersion Version { get; } = DtsodVersion.V30; public IDictionary ToDictionary() => this; public DtsodV30() : base() => UpdateLazy(); public DtsodV30(IDictionary dict) : base(dict) => UpdateLazy(); public DtsodV30(string serialized) : this() => Append(Deserialize(serialized)); static IDictionary Deserialize(string text) { char c; int i = -1; // ++i в ReadType StringBuilder b = new(); Type ReadType() { while (i < text.Length - 1) { c = text[++i]; switch (c) { case ' ': case '\t': case '\r': case '\n': break; case '#': SkipComment(); break; case ':': string _type = b.ToString(); b.Clear(); return TypeHelper.Instance.TypeFromString(_type); case '=': case '"': case '\'': case ';': case '[': case ']': case '{': case '}': throw new Exception($"DtsodV30.Deserialize() error: unexpected {c}"); default: b.Append(c); break; } } throw new Exception("DtsodV30.Deserialize.ReadType() error: end of text\ntext:\n" + text); } string ReadName() { while (i < text.Length - 1) { c = text[++i]; switch (c) { case ' ': case '\t': case '\r': case '\n': break; case '#': SkipComment(); break; case '=': string _name = b.ToString(); b.Clear(); return _name; case ':': case '"': case '\'': case ';': case '[': case ']': case '{': case '}': throw new Exception($"DtsodV30.Deserialize() error: unexpected {c}"); default: b.Append(c); break; } } throw new Exception("DtsodV30.Deserialize.ReadName() error: end of text\ntext:\n" + text); } object ReadValue(ref bool endoflist) { void ReadString() { c = text[++i]; //пропускает начальный символ '"' while ((c != '"' && c != '\'') || (text[i - 1] == '\\' && text[i - 2] != '\\')) { b.Append(c); if (++i >= text.Length) throw new Exception("DtsodV30.Deserialize() error: end of text\ntext:\n" + text); c = text[i]; } } List ReadList() { List list = new(); bool _endoflist = false; while (!_endoflist) list.Add(CreateInstance(ReadType(), ReadValue(ref _endoflist))); b.Clear(); return list; } IDictionary ReadDictionary() { short bracketBalance = 1; c = text[++i]; //пропускает начальный символ '{' while (bracketBalance != 0) { switch (c) { case ' ': case '\t': case '\r': case '\n': break; case '#': SkipComment(); break; case '{': bracketBalance++; b.Append(c); break; case '}': bracketBalance--; if (bracketBalance != 0) b.Append(c); break; case '"': case '\'': b.Append('"'); ReadString(); b.Append('"'); break; default: b.Append(c); break; } if (++i >= text.Length) throw new Exception("DtsodV30.Deserialize() error: end of text\ntext:\n" + text); c = text[i]; } b.Clear(); return Deserialize(b.ToString()); } while (i < text.Length-1) { c = text[++i]; switch (c) { case ' ': case '\t': case '\r': case '\n': break; case '#': SkipComment(); break; case '"': case '\'': ReadString(); break; case ';': // один параметр case ',': // для листов string str = b.ToString(); b.Clear(); return str == "null" ? null : str; case '[': return ReadList(); case ']': endoflist = true; goto case ','; case '{': return ReadDictionary(); case '=': case ':': case '}': throw new Exception($"DtsodV30.Deserialize() error: unexpected {c}"); default: b.Append(c); break; } } throw new Exception("DtsodV30.Deserialize.ReadValue() error: end of text\ntext:\n" + text); } void SkipComment() { while (text[i] != '\n') if (++i >= text.Length) throw new Exception("DtsodV30.Deserialize() error: end of text\ntext:\n" + text); } object CreateInstance(Type type, object ctor_arg) { if (TypeHelper.Instance.BaseTypeConstructors.TryGetValue(type, out Func ctor)) return (object)ctor.Invoke((string)ctor_arg); else if (type.CustomAttributes.Any(a => a.AttributeType == typeof(DtsodSerializableAttribute))) return Activator.CreateInstance(type, ((IDictionary)ctor_arg).Values.ToArray()); else if (typeof(ICollection).IsAssignableFrom(type)) { var method_As = typeof(TypeHelper).GetMethod("As", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic) .MakeGenericMethod(type.GetGenericArguments()[0]); object collection = type.GetConstructor(Type.EmptyTypes).Invoke(null); var method_Add = type.GetMethod("Add"); Log(method_Add.Name); foreach (object el in (IEnumerable)ctor_arg) { var pel = method_As.Invoke(null, new object[] { el }); method_Add.Invoke(collection, new object[] { pel }); } return collection; } else throw new Exception($"can't create instance of {type.FullName}"); } Dictionary output = new(); Type type; string name; object value; for (; i < text.Length; i++) { type = ReadType(); name = ReadName(); bool _ = false; value = ReadValue(ref _); output.Add(name, CreateInstance(type, value)); } return output; } public override void Append(ICollection> anotherDtsod) => base.Append(anotherDtsod);//UpdateLazy(); public override void Add(string key, dynamic value) => base.Add(key, (object)value);//UpdateLazy(); protected static string Serialize(IDictionary dtsod, ushort tabsCount = 0) { StringBuilder b = new(); foreach (KeyValuePair pair in dtsod) { } void SerializeObject(string name, dynamic inst) { Type type = inst.GetType(); b.Append(TypeHelper.Instance.TypeToString(type)).Append(':') .Append(name).Append('='); if (TypeHelper.Instance.BaseTypeNames.ContainsKey(type)) { if (type == typeof(decimal) || type == typeof(double) || type == typeof(float)) b.Append(inst.ToString(CultureInfo.InvariantCulture)); else b.Append(inst.ToString()); } else if (typeof(IDictionary).IsAssignableFrom(type)) b.Append("\n{\n").Append(Serialize(inst, tabsCount++)).Append("};\n"); else if (type.CustomAttributes.Any(a => a.AttributeType == typeof(DtsodSerializableAttribute))) { var props = type.GetProperties().Where(p => p.CustomAttributes.Any(a => a.AttributeType == typeof(DtsodSerializableAttribute))); foreach (var prop in props) { var propval = prop.GetValue(inst); } } else throw new Exception($"can't serialize type {type.FullName}"); } return b.ToString(); } protected Lazy serialized; protected void UpdateLazy() => serialized = new(() => Serialize(this)); public override string ToString() => serialized.Value; }