/RouterOS.ClassBuilder/TypeDefinition.cs
C# | 497 lines | 427 code | 50 blank | 20 comment | 136 complexity | 9077184b3f9a501505cd9e02391b6f16 MD5 | raw file
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Text.RegularExpressions;
-
- namespace RouterOS.ClassBuilder
- {
- public class TypeInfo
- {
- public string Name { get; set; }
- public bool IsValueType { get; set; }
-
- public static implicit operator TypeInfo(string str)
- {
- return new TypeInfo() { Name = str };
- }
-
- public static implicit operator TypeInfo(Type type)
- {
- return new TypeInfo() { Name = type.Name, IsValueType = type.IsValueType };
- }
-
- public override string ToString()
- {
- if (IsValueType)
- return Name + "?";
- return Name;
- }
- }
-
- public class TypeDefinition
- {
- public string Name { get; set; }
- public string Definition { get; set; }
- public List<TypeDefinition> SubDefintions { get; set; }
- public Dictionary<string, string> StaticValues { get; internal set; }
- public TypeDefinition Parent { get; set; }
-
- public TypeDefinition()
- {
- SubDefintions = new List<TypeDefinition>();
- StaticValues = new Dictionary<string, string>();
- }
-
- private static void BuildOneOfType(StringBuilder sb, int level, string typeName, Dictionary<string, TypeInfo> dataTypes)
- {
- string OneOfTypeVar = @"public {1} {2}
- {{
- get {{ return value as {1}; }}
- set {{ this.value = value; }}
- }}
- public static implicit operator {0}({1} value)
- {{
- return new {0}() {{ value = value }};
- }}";
-
- string OneOfTypeParse = @"
- try {{ return new {0}() {{ value = TypeFormatter.ConvertFromString<{1}>(str, conn) }}; }}
- catch(Exception) {{ }}";
-
- string OneOfType = @"public struct {0}
- {{
- private object value;
- {1}
- public static {0} Parse(string str, Connection conn)
- {{
- {2}
- throw new NotSupportedException();
- }}
- public override string ToString() {{ return value != null ? value.ToString() : null; }}
- }}";
-
- string str = "", parse = "";
- foreach (var dataType in dataTypes)
- {
- str += String.Format(OneOfTypeVar, typeName, dataType.Value, dataType.Key);
- parse += String.Format(OneOfTypeParse, typeName, dataType.Value.Name, dataType.Key);
- }
- sb.WriteLine(level, OneOfType, typeName, str, parse);
- }
-
- private static void BuildParser(StringBuilder sb, int level, string typeName, string dataTypeName, string suffix)
- {
- string form = @"public struct {0}
- {{
- public {1} Value {{ get; set; }}
-
- public static {0} Parse(string str)
- {{
- if (str.EndsWith(""{2}""))
- str = str.Substring(0, str.Length - ""{2}"".Length);
- return new Parser() {{ Value = TypeFormatter.ConvertFromString<{1}>(str, null) }};
- }}
-
- public override string ToString()
- {{
- return String.Concat(Value, ""{2}"");
- }}
-
- public static implicit operator {0}({1} value)
- {{
- return new Parser() {{ Value = value }};
- }}
-
- public static implicit operator {1}({0} parser)
- {{
- return parser.Value;
- }}
- }}";
-
- sb.WriteLine(level, form, typeName, dataTypeName, suffix);
- }
-
- private static void BuildTwoType(StringBuilder sb, int level, string typeName, string firstTypeName, string firstName, string secondTypeName, string secondName, char separator)
- {
- string form = @"public struct {0}
- {{
- public {1} {2} {{ get; set; }}
- public {3} {4} {{ get; set; }}
-
- public static {0} Parse(string str)
- {{
- string[] strs = str.Split('{5}');
- if (strs.Length == 2)
- return new {0}() {{ {2} = TypeFormatter.ConvertFromString<{1}>(strs[0], null), {4} = TypeFormatter.ConvertFromString<{3}>(strs[1], null) }};
- else
- throw new InvalidOperationException();
- }}
-
- public override string ToString()
- {{
- return String.Concat({2}, '{5}', {4});
- }}
- }}";
-
- sb.WriteLine(level, form, typeName, firstTypeName, firstName, secondTypeName, secondName, separator);
- }
-
- private static void BuildOptionalTwoType(StringBuilder sb, int level, string typeName, string firstTypeName, string firstName, string secondTypeName, string secondName, char separator)
- {
- string form = @"public struct {0}
- {{
- public {1} {2} {{ get; set; }}
- public {3} {4} {{ get; set; }}
-
- public static {0} Parse(string str)
- {{
- string[] strs = str.Split('{5}');
- if (strs.Length == 2)
- return new {0}() {{ {2} = TypeFormatter.ConvertFromString<{1}>(strs[0], null), {4} = TypeFormatter.ConvertFromString<{3}>(strs[1], null) }};
- else if (strs.Length == 1)
- return new {0}() {{ {2} = TypeFormatter.ConvertFromString<{1}>(strs[0], null) }};
- else
- throw new InvalidOperationException();
- }}
-
- public override string ToString()
- {{
- if({4} != null)
- return String.Concat({2}, '{5}', {4});
- else
- return {2}.ToString();
- }}
- }}";
-
- sb.WriteLine(level, form, typeName, firstTypeName, firstName, secondTypeName, secondName, separator);
- }
-
- private static readonly Regex CheckNameRegex = new Regex("^[!a-zA-Z0-9-]+$");
-
- public TypeInfo BuildType(StringBuilder sb, int level, SortedDictionary<string, bool> typeList, IEnumerable<ObjectDefinition> allObjects)
- {
- // many values
- if (SubDefintions.Count == 1 && Definition.Split(' ')[0] == String.Format("{0}[,{1}]", SubDefintions[0].Name, Name))
- {
- string typeName = SubDefintions[0].BuildType(sb, level, typeList, allObjects).Name;
- return typeName + "[]";
- }
-
- if (SubDefintions.Count == 1 && Definition == String.Format("{0}[%]", SubDefintions[0].Name))
- {
- string dataType = SubDefintions[0].BuildType(sb, level, typeList, allObjects).Name;
- string typeName = Name.ConvertToName() + "Type";
- if (!typeList.ContainsKey(typeName))
- {
- BuildParser(sb, level, typeName, dataType, "%");
- typeList[typeName] = true;
- }
- return new TypeInfo() { Name = typeName, IsValueType = true };
- }
-
- if (SubDefintions.Count == 1 && Definition == String.Format("{0}[bps]", SubDefintions[0].Name))
- {
- return "Bits";
- }
-
-
- #if dasdsa
- if (Subs.Count == 1 && Def == String.Format("[!]{0}", Subs[0].Name))
- {
- string type = Subs[0].BuildType(sb, level, typeList, allObjects);
- return String.Format("Neg<{0}>", type);
- }
- #endif
-
- if (SubDefintions.Count == 2 && Definition == String.Format("{0}:{1}", SubDefintions[0].Name, SubDefintions[1].Name))
- {
- string firstType = SubDefintions[0].BuildType(sb, level, typeList, allObjects).Name;
- string secondType = SubDefintions[1].BuildType(sb, level, typeList, allObjects).Name;
- string typeName = Name.ConvertToName() + "Type";
- if (!typeList.ContainsKey(typeName))
- {
- BuildTwoType(sb, level, typeName, firstType, SubDefintions[0].Name, secondType, SubDefintions[1].Name, ':');
- typeList[typeName] = true;
- }
- return new TypeInfo() { Name = typeName, IsValueType = true };
- }
-
- if (SubDefintions.Count == 2 && Definition == String.Format("{0}/{1}", SubDefintions[0].Name, SubDefintions[1].Name))
- {
- string firstType = SubDefintions[0].BuildType(sb, level, typeList, allObjects).Name;
- string secondType = SubDefintions[1].BuildType(sb, level, typeList, allObjects).Name;
- string typeName = Name.ConvertToName() + "Type";
- if (!typeList.ContainsKey(typeName))
- {
- BuildTwoType(sb, level, typeName, firstType, SubDefintions[0].Name, secondType, SubDefintions[1].Name, '/');
- typeList[typeName] = true;
- }
- return new TypeInfo() { Name = typeName, IsValueType = true };
- }
-
- if (SubDefintions.Count == 2 && Definition == String.Format("{0}[-{1}]", SubDefintions[0].Name, SubDefintions[1].Name))
- {
- string firstType = SubDefintions[0].BuildType(sb, level, typeList, allObjects).Name;
- string secondType = SubDefintions[1].BuildType(sb, level, typeList, allObjects).Name;
- string typeName = Name.ConvertToName() + "Type";
- if (!typeList.ContainsKey(typeName))
- {
- BuildOptionalTwoType(sb, level, typeName, firstType, SubDefintions[0].Name, secondType, SubDefintions[1].Name, '-');
- typeList[typeName] = true;
- }
- return new TypeInfo() { Name = typeName, IsValueType = true };
- }
-
- if (Definition.Contains("integer number"))
- {
- if (Definition.Contains("0..18446744073709551615"))
- return typeof(Int64);
- else if (Definition.Contains("0..65536"))
- return typeof(UInt16);
- else
- return typeof(Int32);
- }
-
- // some standard types
- if (Definition == "yes | no")
- return typeof(Boolean);
- if (Definition.Contains("decimal number"))
- return typeof(Single);
- if (Definition == "802.2")
- return typeof(Byte);
- if (Definition == "ip")
- return typeof(UInt16);
- if (Definition == "0..65535")
- return typeof(UInt16);
- if (Definition.Contains("time interval"))
- return typeof(TimeSpan);
- if (Definition.Contains("IP address"))
- return "IPAddress";
- if (Name.StartsWith("Ipv6") && Definition == "see documentation")
- return "IPv6Address";
- if (Definition == "Month/Day/Year Hour:Min:Sec" || Definition == "Hour:Min:Sec" || Definition == "Month/Day/Year")
- return typeof(DateTime);
- if (Definition.Contains("IP address range"))
- return new TypeInfo() { Name = "IPRange", IsValueType = true };
- if (Definition.Contains("IP prefix"))
- return new TypeInfo() { Name = "IPPrefix", IsValueType = true };
- if (Definition.Contains("IPv6 prefix"))
- return new TypeInfo() { Name = "IPv6Prefix", IsValueType = true };
- if (Definition.Contains("MAC address"))
- return new TypeInfo() { Name = "MACAddress", IsValueType = true };
- if (Definition.Contains("string value"))
- return typeof(String);
-
- // one of many enumeration
- string[] values = Definition.Split(new string[] { " | " }, StringSplitOptions.RemoveEmptyEntries);
- if (values.Length == 0)
- throw new NotSupportedException();
-
- // create base types
- Dictionary<string, TypeInfo> dataTypes = new Dictionary<string, TypeInfo>();
- Dictionary<string, string> enumValues = new Dictionary<string, string>();
- List<string> specialValues = new List<string>();
- int offset = 0;
-
- // check each name
- foreach (string value in values)
- {
- // all enumeration values
- if (value == "...")
- {
- foreach (var e in StaticValues)
- {
- if (e.Key[0] == '!')
- specialValues.Add(e.Key);
- else if (e.Value.StartsWith("string value"))
- dataTypes.Add(value, "String");
- else if (e.Value.StartsWith("time interval"))
- dataTypes.Add(value, new TypeInfo() { Name = "TimeSpan", IsValueType = true });
- else if (e.Value.StartsWith("integer number"))
- dataTypes.Add(value, new TypeInfo() { Name = "Int32", IsValueType = true });
- else
- enumValues[e.Key] = e.Value;
- }
- continue;
- }
-
- // is enum? check special type
- if (StaticValues.ContainsKey(value))
- {
- // special enumeration descriptions
- if (StaticValues[value].StartsWith("string value"))
- dataTypes.Add(value, "String");
- else if (StaticValues[value].StartsWith("time interval"))
- dataTypes.Add(value, new TypeInfo() { Name = "TimeSpan", IsValueType = true });
- else if (StaticValues[value].StartsWith("integer number"))
- dataTypes.Add(value, new TypeInfo() { Name = "Int32", IsValueType = true });
- else
- enumValues.Add(value, StaticValues[value]);
- continue;
- }
-
- // check if name is valid
- if (!CheckNameRegex.IsMatch(value))
- throw new NotSupportedException(value);
-
- // special case when there are not subcase definitions
- if (SubDefintions.Count == 0)
- {
- if (value[0] == '!')
- specialValues.Add(value);
- else
- enumValues.Add(value, "");
- continue;
- }
-
- // find next definition
- IEnumerable<TypeDefinition> subListFromOffset = SubDefintions.Skip(offset).Concat(SubDefintions.Take(offset));
- TypeDefinition otherInfo = subListFromOffset.FirstOrDefault(d => d.Name == value);
-
- // check definition
- if (otherInfo == null)
- {
- // special case when is probable enumeration value
- if (value.ToLower() != value)
- throw new NotSupportedException();
- if (value[0] == '!')
- specialValues.Add(value);
- else
- enumValues.Add(value, "");
- continue;
- }
-
- // get offset of value
- offset = SubDefintions.IndexOf(otherInfo);
- string name = otherInfo.Name.ConvertToName();
- for (int i = 2; dataTypes.ContainsKey(name); ++i)
- name = otherInfo.Name.ConvertToName() + i;
- dataTypes.Add(name, otherInfo.BuildType(sb, level, typeList, allObjects));
- }
-
- // find special value
- if (specialValues.Contains("!use-string"))
- {
- dataTypes.Add(Name.ConvertToName(), typeof(String));
- specialValues.Clear();
- enumValues.Clear();
- }
- else if (specialValues.Count != 0)
- {
- ObjectDefinition objectInfo = allObjects.FirstOrDefault(o => specialValues.All(sv => o.Actual != null && o.Actual.FirstOrDefault(oa => oa.Name == sv) != null));
- if (objectInfo == null)
- throw new NotSupportedException("couldn't map specialValues to object");
- dataTypes.Add(objectInfo.ClassName, objectInfo.ClassScope);
-
- // remove predefined values
- if (objectInfo.Predefined != null)
- {
- foreach (var actual in objectInfo.Predefined)
- enumValues.Remove(actual.Name);
- }
- }
-
- // build enum values
- if (enumValues.Count != 0)
- {
- ObjectDefinition objectInfo = allObjects.FirstOrDefault(o => enumValues.All(sv => o.Actual != null && o.Actual.FirstOrDefault(oa => oa.Name == sv.Key) != null));
- if (objectInfo != null)
- {
- // we found matching type! yay!
- if (!dataTypes.ContainsKey(objectInfo.ClassName))
- {
- dataTypes.Add(objectInfo.ClassName, objectInfo.ClassScope);
- }
- }
- else
- {
- string enumType = Name.ConvertToName() + "Enum";
- if (!typeList.ContainsKey(enumType))
- {
- enumValues.ToEnumTypes().BuildEnum(sb, level, enumType, false);
- typeList[enumType] = true;
- }
- dataTypes.Add("State", new TypeInfo() { Name = enumType, IsValueType = true });
- }
- }
-
- // when one, don't generate class
- if (dataTypes.Count == 1)
- return dataTypes.First().Value;
-
- // generate OneOfType
- string typeName2 = Name.ConvertToName() + "Type";
- if (!typeList.ContainsKey(typeName2))
- {
- BuildOneOfType(sb, level, typeName2, dataTypes);
- typeList[typeName2] = true;
- }
- return new TypeInfo() { Name = typeName2, IsValueType = true };
- }
-
- public static TypeDefinition Parse(string[] lines)
- {
- TypeDefinition root = null;
- TypeDefinition last = null;
-
- foreach (string line in lines)
- {
- if (line.Contains(" ::= "))
- {
- string[] defs = line.Split(new string[] { " ::= " }, StringSplitOptions.None);
- if (defs.Length != 2)
- throw new NotSupportedException("too many elements");
-
- TypeDefinition newDef = null;
- List<TypeDefinition> subs = null;
-
- foreach (string name in defs[0].Trim().Split(','))
- {
- newDef = new TypeDefinition() { Name = name, Definition = defs[1].Trim() };
- if (subs == null)
- subs = newDef.SubDefintions;
- else
- newDef.SubDefintions = subs;
-
- if (root == null)
- {
- root = newDef;
- }
- else
- {
- newDef.Parent = last;
- do
- {
- if (newDef.Parent.Definition.Contains(newDef.Name))
- {
- newDef.Parent.SubDefintions.Add(newDef);
- break;
- }
- newDef.Parent = newDef.Parent.Parent;
- }
- while (newDef.Parent != null);
-
- if (newDef.Parent == null)
- throw new NotSupportedException("parent not found");
- }
- }
-
- last = newDef;
- }
- else if (line.Contains(" -- "))
- {
- string[] enums = line.Split(new string[] { " -- " }, StringSplitOptions.None);
- if (enums.Length != 2)
- throw new NotSupportedException("too many elements");
- if (last == null)
- last = root = new TypeDefinition() { Name = enums[0].Trim(), Definition = "..." };
-
- last.StaticValues[enums[0].Trim()] = enums[1].Trim();
- }
- }
-
- return root;
- }
- }
- }