PageRenderTime 49ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/RouterOS.ClassBuilder/TypeDefinition.cs

https://bitbucket.org/ayufan/rosapi-csharp
C# | 497 lines | 427 code | 50 blank | 20 comment | 136 complexity | 9077184b3f9a501505cd9e02391b6f16 MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Text.RegularExpressions;
  6. namespace RouterOS.ClassBuilder
  7. {
  8. public class TypeInfo
  9. {
  10. public string Name { get; set; }
  11. public bool IsValueType { get; set; }
  12. public static implicit operator TypeInfo(string str)
  13. {
  14. return new TypeInfo() { Name = str };
  15. }
  16. public static implicit operator TypeInfo(Type type)
  17. {
  18. return new TypeInfo() { Name = type.Name, IsValueType = type.IsValueType };
  19. }
  20. public override string ToString()
  21. {
  22. if (IsValueType)
  23. return Name + "?";
  24. return Name;
  25. }
  26. }
  27. public class TypeDefinition
  28. {
  29. public string Name { get; set; }
  30. public string Definition { get; set; }
  31. public List<TypeDefinition> SubDefintions { get; set; }
  32. public Dictionary<string, string> StaticValues { get; internal set; }
  33. public TypeDefinition Parent { get; set; }
  34. public TypeDefinition()
  35. {
  36. SubDefintions = new List<TypeDefinition>();
  37. StaticValues = new Dictionary<string, string>();
  38. }
  39. private static void BuildOneOfType(StringBuilder sb, int level, string typeName, Dictionary<string, TypeInfo> dataTypes)
  40. {
  41. string OneOfTypeVar = @"public {1} {2}
  42. {{
  43. get {{ return value as {1}; }}
  44. set {{ this.value = value; }}
  45. }}
  46. public static implicit operator {0}({1} value)
  47. {{
  48. return new {0}() {{ value = value }};
  49. }}";
  50. string OneOfTypeParse = @"
  51. try {{ return new {0}() {{ value = TypeFormatter.ConvertFromString<{1}>(str, conn) }}; }}
  52. catch(Exception) {{ }}";
  53. string OneOfType = @"public struct {0}
  54. {{
  55. private object value;
  56. {1}
  57. public static {0} Parse(string str, Connection conn)
  58. {{
  59. {2}
  60. throw new NotSupportedException();
  61. }}
  62. public override string ToString() {{ return value != null ? value.ToString() : null; }}
  63. }}";
  64. string str = "", parse = "";
  65. foreach (var dataType in dataTypes)
  66. {
  67. str += String.Format(OneOfTypeVar, typeName, dataType.Value, dataType.Key);
  68. parse += String.Format(OneOfTypeParse, typeName, dataType.Value.Name, dataType.Key);
  69. }
  70. sb.WriteLine(level, OneOfType, typeName, str, parse);
  71. }
  72. private static void BuildParser(StringBuilder sb, int level, string typeName, string dataTypeName, string suffix)
  73. {
  74. string form = @"public struct {0}
  75. {{
  76. public {1} Value {{ get; set; }}
  77. public static {0} Parse(string str)
  78. {{
  79. if (str.EndsWith(""{2}""))
  80. str = str.Substring(0, str.Length - ""{2}"".Length);
  81. return new Parser() {{ Value = TypeFormatter.ConvertFromString<{1}>(str, null) }};
  82. }}
  83. public override string ToString()
  84. {{
  85. return String.Concat(Value, ""{2}"");
  86. }}
  87. public static implicit operator {0}({1} value)
  88. {{
  89. return new Parser() {{ Value = value }};
  90. }}
  91. public static implicit operator {1}({0} parser)
  92. {{
  93. return parser.Value;
  94. }}
  95. }}";
  96. sb.WriteLine(level, form, typeName, dataTypeName, suffix);
  97. }
  98. private static void BuildTwoType(StringBuilder sb, int level, string typeName, string firstTypeName, string firstName, string secondTypeName, string secondName, char separator)
  99. {
  100. string form = @"public struct {0}
  101. {{
  102. public {1} {2} {{ get; set; }}
  103. public {3} {4} {{ get; set; }}
  104. public static {0} Parse(string str)
  105. {{
  106. string[] strs = str.Split('{5}');
  107. if (strs.Length == 2)
  108. return new {0}() {{ {2} = TypeFormatter.ConvertFromString<{1}>(strs[0], null), {4} = TypeFormatter.ConvertFromString<{3}>(strs[1], null) }};
  109. else
  110. throw new InvalidOperationException();
  111. }}
  112. public override string ToString()
  113. {{
  114. return String.Concat({2}, '{5}', {4});
  115. }}
  116. }}";
  117. sb.WriteLine(level, form, typeName, firstTypeName, firstName, secondTypeName, secondName, separator);
  118. }
  119. private static void BuildOptionalTwoType(StringBuilder sb, int level, string typeName, string firstTypeName, string firstName, string secondTypeName, string secondName, char separator)
  120. {
  121. string form = @"public struct {0}
  122. {{
  123. public {1} {2} {{ get; set; }}
  124. public {3} {4} {{ get; set; }}
  125. public static {0} Parse(string str)
  126. {{
  127. string[] strs = str.Split('{5}');
  128. if (strs.Length == 2)
  129. return new {0}() {{ {2} = TypeFormatter.ConvertFromString<{1}>(strs[0], null), {4} = TypeFormatter.ConvertFromString<{3}>(strs[1], null) }};
  130. else if (strs.Length == 1)
  131. return new {0}() {{ {2} = TypeFormatter.ConvertFromString<{1}>(strs[0], null) }};
  132. else
  133. throw new InvalidOperationException();
  134. }}
  135. public override string ToString()
  136. {{
  137. if({4} != null)
  138. return String.Concat({2}, '{5}', {4});
  139. else
  140. return {2}.ToString();
  141. }}
  142. }}";
  143. sb.WriteLine(level, form, typeName, firstTypeName, firstName, secondTypeName, secondName, separator);
  144. }
  145. private static readonly Regex CheckNameRegex = new Regex("^[!a-zA-Z0-9-]+$");
  146. public TypeInfo BuildType(StringBuilder sb, int level, SortedDictionary<string, bool> typeList, IEnumerable<ObjectDefinition> allObjects)
  147. {
  148. // many values
  149. if (SubDefintions.Count == 1 && Definition.Split(' ')[0] == String.Format("{0}[,{1}]", SubDefintions[0].Name, Name))
  150. {
  151. string typeName = SubDefintions[0].BuildType(sb, level, typeList, allObjects).Name;
  152. return typeName + "[]";
  153. }
  154. if (SubDefintions.Count == 1 && Definition == String.Format("{0}[%]", SubDefintions[0].Name))
  155. {
  156. string dataType = SubDefintions[0].BuildType(sb, level, typeList, allObjects).Name;
  157. string typeName = Name.ConvertToName() + "Type";
  158. if (!typeList.ContainsKey(typeName))
  159. {
  160. BuildParser(sb, level, typeName, dataType, "%");
  161. typeList[typeName] = true;
  162. }
  163. return new TypeInfo() { Name = typeName, IsValueType = true };
  164. }
  165. if (SubDefintions.Count == 1 && Definition == String.Format("{0}[bps]", SubDefintions[0].Name))
  166. {
  167. return "Bits";
  168. }
  169. #if dasdsa
  170. if (Subs.Count == 1 && Def == String.Format("[!]{0}", Subs[0].Name))
  171. {
  172. string type = Subs[0].BuildType(sb, level, typeList, allObjects);
  173. return String.Format("Neg<{0}>", type);
  174. }
  175. #endif
  176. if (SubDefintions.Count == 2 && Definition == String.Format("{0}:{1}", SubDefintions[0].Name, SubDefintions[1].Name))
  177. {
  178. string firstType = SubDefintions[0].BuildType(sb, level, typeList, allObjects).Name;
  179. string secondType = SubDefintions[1].BuildType(sb, level, typeList, allObjects).Name;
  180. string typeName = Name.ConvertToName() + "Type";
  181. if (!typeList.ContainsKey(typeName))
  182. {
  183. BuildTwoType(sb, level, typeName, firstType, SubDefintions[0].Name, secondType, SubDefintions[1].Name, ':');
  184. typeList[typeName] = true;
  185. }
  186. return new TypeInfo() { Name = typeName, IsValueType = true };
  187. }
  188. if (SubDefintions.Count == 2 && Definition == String.Format("{0}/{1}", SubDefintions[0].Name, SubDefintions[1].Name))
  189. {
  190. string firstType = SubDefintions[0].BuildType(sb, level, typeList, allObjects).Name;
  191. string secondType = SubDefintions[1].BuildType(sb, level, typeList, allObjects).Name;
  192. string typeName = Name.ConvertToName() + "Type";
  193. if (!typeList.ContainsKey(typeName))
  194. {
  195. BuildTwoType(sb, level, typeName, firstType, SubDefintions[0].Name, secondType, SubDefintions[1].Name, '/');
  196. typeList[typeName] = true;
  197. }
  198. return new TypeInfo() { Name = typeName, IsValueType = true };
  199. }
  200. if (SubDefintions.Count == 2 && Definition == String.Format("{0}[-{1}]", SubDefintions[0].Name, SubDefintions[1].Name))
  201. {
  202. string firstType = SubDefintions[0].BuildType(sb, level, typeList, allObjects).Name;
  203. string secondType = SubDefintions[1].BuildType(sb, level, typeList, allObjects).Name;
  204. string typeName = Name.ConvertToName() + "Type";
  205. if (!typeList.ContainsKey(typeName))
  206. {
  207. BuildOptionalTwoType(sb, level, typeName, firstType, SubDefintions[0].Name, secondType, SubDefintions[1].Name, '-');
  208. typeList[typeName] = true;
  209. }
  210. return new TypeInfo() { Name = typeName, IsValueType = true };
  211. }
  212. if (Definition.Contains("integer number"))
  213. {
  214. if (Definition.Contains("0..18446744073709551615"))
  215. return typeof(Int64);
  216. else if (Definition.Contains("0..65536"))
  217. return typeof(UInt16);
  218. else
  219. return typeof(Int32);
  220. }
  221. // some standard types
  222. if (Definition == "yes | no")
  223. return typeof(Boolean);
  224. if (Definition.Contains("decimal number"))
  225. return typeof(Single);
  226. if (Definition == "802.2")
  227. return typeof(Byte);
  228. if (Definition == "ip")
  229. return typeof(UInt16);
  230. if (Definition == "0..65535")
  231. return typeof(UInt16);
  232. if (Definition.Contains("time interval"))
  233. return typeof(TimeSpan);
  234. if (Definition.Contains("IP address"))
  235. return "IPAddress";
  236. if (Name.StartsWith("Ipv6") && Definition == "see documentation")
  237. return "IPv6Address";
  238. if (Definition == "Month/Day/Year Hour:Min:Sec" || Definition == "Hour:Min:Sec" || Definition == "Month/Day/Year")
  239. return typeof(DateTime);
  240. if (Definition.Contains("IP address range"))
  241. return new TypeInfo() { Name = "IPRange", IsValueType = true };
  242. if (Definition.Contains("IP prefix"))
  243. return new TypeInfo() { Name = "IPPrefix", IsValueType = true };
  244. if (Definition.Contains("IPv6 prefix"))
  245. return new TypeInfo() { Name = "IPv6Prefix", IsValueType = true };
  246. if (Definition.Contains("MAC address"))
  247. return new TypeInfo() { Name = "MACAddress", IsValueType = true };
  248. if (Definition.Contains("string value"))
  249. return typeof(String);
  250. // one of many enumeration
  251. string[] values = Definition.Split(new string[] { " | " }, StringSplitOptions.RemoveEmptyEntries);
  252. if (values.Length == 0)
  253. throw new NotSupportedException();
  254. // create base types
  255. Dictionary<string, TypeInfo> dataTypes = new Dictionary<string, TypeInfo>();
  256. Dictionary<string, string> enumValues = new Dictionary<string, string>();
  257. List<string> specialValues = new List<string>();
  258. int offset = 0;
  259. // check each name
  260. foreach (string value in values)
  261. {
  262. // all enumeration values
  263. if (value == "...")
  264. {
  265. foreach (var e in StaticValues)
  266. {
  267. if (e.Key[0] == '!')
  268. specialValues.Add(e.Key);
  269. else if (e.Value.StartsWith("string value"))
  270. dataTypes.Add(value, "String");
  271. else if (e.Value.StartsWith("time interval"))
  272. dataTypes.Add(value, new TypeInfo() { Name = "TimeSpan", IsValueType = true });
  273. else if (e.Value.StartsWith("integer number"))
  274. dataTypes.Add(value, new TypeInfo() { Name = "Int32", IsValueType = true });
  275. else
  276. enumValues[e.Key] = e.Value;
  277. }
  278. continue;
  279. }
  280. // is enum? check special type
  281. if (StaticValues.ContainsKey(value))
  282. {
  283. // special enumeration descriptions
  284. if (StaticValues[value].StartsWith("string value"))
  285. dataTypes.Add(value, "String");
  286. else if (StaticValues[value].StartsWith("time interval"))
  287. dataTypes.Add(value, new TypeInfo() { Name = "TimeSpan", IsValueType = true });
  288. else if (StaticValues[value].StartsWith("integer number"))
  289. dataTypes.Add(value, new TypeInfo() { Name = "Int32", IsValueType = true });
  290. else
  291. enumValues.Add(value, StaticValues[value]);
  292. continue;
  293. }
  294. // check if name is valid
  295. if (!CheckNameRegex.IsMatch(value))
  296. throw new NotSupportedException(value);
  297. // special case when there are not subcase definitions
  298. if (SubDefintions.Count == 0)
  299. {
  300. if (value[0] == '!')
  301. specialValues.Add(value);
  302. else
  303. enumValues.Add(value, "");
  304. continue;
  305. }
  306. // find next definition
  307. IEnumerable<TypeDefinition> subListFromOffset = SubDefintions.Skip(offset).Concat(SubDefintions.Take(offset));
  308. TypeDefinition otherInfo = subListFromOffset.FirstOrDefault(d => d.Name == value);
  309. // check definition
  310. if (otherInfo == null)
  311. {
  312. // special case when is probable enumeration value
  313. if (value.ToLower() != value)
  314. throw new NotSupportedException();
  315. if (value[0] == '!')
  316. specialValues.Add(value);
  317. else
  318. enumValues.Add(value, "");
  319. continue;
  320. }
  321. // get offset of value
  322. offset = SubDefintions.IndexOf(otherInfo);
  323. string name = otherInfo.Name.ConvertToName();
  324. for (int i = 2; dataTypes.ContainsKey(name); ++i)
  325. name = otherInfo.Name.ConvertToName() + i;
  326. dataTypes.Add(name, otherInfo.BuildType(sb, level, typeList, allObjects));
  327. }
  328. // find special value
  329. if (specialValues.Contains("!use-string"))
  330. {
  331. dataTypes.Add(Name.ConvertToName(), typeof(String));
  332. specialValues.Clear();
  333. enumValues.Clear();
  334. }
  335. else if (specialValues.Count != 0)
  336. {
  337. ObjectDefinition objectInfo = allObjects.FirstOrDefault(o => specialValues.All(sv => o.Actual != null && o.Actual.FirstOrDefault(oa => oa.Name == sv) != null));
  338. if (objectInfo == null)
  339. throw new NotSupportedException("couldn't map specialValues to object");
  340. dataTypes.Add(objectInfo.ClassName, objectInfo.ClassScope);
  341. // remove predefined values
  342. if (objectInfo.Predefined != null)
  343. {
  344. foreach (var actual in objectInfo.Predefined)
  345. enumValues.Remove(actual.Name);
  346. }
  347. }
  348. // build enum values
  349. if (enumValues.Count != 0)
  350. {
  351. ObjectDefinition objectInfo = allObjects.FirstOrDefault(o => enumValues.All(sv => o.Actual != null && o.Actual.FirstOrDefault(oa => oa.Name == sv.Key) != null));
  352. if (objectInfo != null)
  353. {
  354. // we found matching type! yay!
  355. if (!dataTypes.ContainsKey(objectInfo.ClassName))
  356. {
  357. dataTypes.Add(objectInfo.ClassName, objectInfo.ClassScope);
  358. }
  359. }
  360. else
  361. {
  362. string enumType = Name.ConvertToName() + "Enum";
  363. if (!typeList.ContainsKey(enumType))
  364. {
  365. enumValues.ToEnumTypes().BuildEnum(sb, level, enumType, false);
  366. typeList[enumType] = true;
  367. }
  368. dataTypes.Add("State", new TypeInfo() { Name = enumType, IsValueType = true });
  369. }
  370. }
  371. // when one, don't generate class
  372. if (dataTypes.Count == 1)
  373. return dataTypes.First().Value;
  374. // generate OneOfType
  375. string typeName2 = Name.ConvertToName() + "Type";
  376. if (!typeList.ContainsKey(typeName2))
  377. {
  378. BuildOneOfType(sb, level, typeName2, dataTypes);
  379. typeList[typeName2] = true;
  380. }
  381. return new TypeInfo() { Name = typeName2, IsValueType = true };
  382. }
  383. public static TypeDefinition Parse(string[] lines)
  384. {
  385. TypeDefinition root = null;
  386. TypeDefinition last = null;
  387. foreach (string line in lines)
  388. {
  389. if (line.Contains(" ::= "))
  390. {
  391. string[] defs = line.Split(new string[] { " ::= " }, StringSplitOptions.None);
  392. if (defs.Length != 2)
  393. throw new NotSupportedException("too many elements");
  394. TypeDefinition newDef = null;
  395. List<TypeDefinition> subs = null;
  396. foreach (string name in defs[0].Trim().Split(','))
  397. {
  398. newDef = new TypeDefinition() { Name = name, Definition = defs[1].Trim() };
  399. if (subs == null)
  400. subs = newDef.SubDefintions;
  401. else
  402. newDef.SubDefintions = subs;
  403. if (root == null)
  404. {
  405. root = newDef;
  406. }
  407. else
  408. {
  409. newDef.Parent = last;
  410. do
  411. {
  412. if (newDef.Parent.Definition.Contains(newDef.Name))
  413. {
  414. newDef.Parent.SubDefintions.Add(newDef);
  415. break;
  416. }
  417. newDef.Parent = newDef.Parent.Parent;
  418. }
  419. while (newDef.Parent != null);
  420. if (newDef.Parent == null)
  421. throw new NotSupportedException("parent not found");
  422. }
  423. }
  424. last = newDef;
  425. }
  426. else if (line.Contains(" -- "))
  427. {
  428. string[] enums = line.Split(new string[] { " -- " }, StringSplitOptions.None);
  429. if (enums.Length != 2)
  430. throw new NotSupportedException("too many elements");
  431. if (last == null)
  432. last = root = new TypeDefinition() { Name = enums[0].Trim(), Definition = "..." };
  433. last.StaticValues[enums[0].Trim()] = enums[1].Trim();
  434. }
  435. }
  436. return root;
  437. }
  438. }
  439. }