diff options
Diffstat (limited to 'src/internal/TablesAndTuples/Program.cs')
-rw-r--r-- | src/internal/TablesAndTuples/Program.cs | 528 |
1 files changed, 528 insertions, 0 deletions
diff --git a/src/internal/TablesAndTuples/Program.cs b/src/internal/TablesAndTuples/Program.cs new file mode 100644 index 00000000..634acf9d --- /dev/null +++ b/src/internal/TablesAndTuples/Program.cs | |||
@@ -0,0 +1,528 @@ | |||
1 | using System; | ||
2 | using System.Collections.Generic; | ||
3 | using System.IO; | ||
4 | using System.Linq; | ||
5 | using System.Text; | ||
6 | using System.Text.RegularExpressions; | ||
7 | using System.Xml.Linq; | ||
8 | using SimpleJson; | ||
9 | |||
10 | namespace TablesAndSymbols | ||
11 | { | ||
12 | class Program | ||
13 | { | ||
14 | static void Main(string[] args) | ||
15 | { | ||
16 | if (args.Length == 0) | ||
17 | { | ||
18 | return; | ||
19 | } | ||
20 | else if (Path.GetExtension(args[0]) == ".xml") | ||
21 | { | ||
22 | if (args.Length < 2) | ||
23 | { | ||
24 | Console.WriteLine("Need to specify output json file as well."); | ||
25 | return; | ||
26 | } | ||
27 | if (Path.GetExtension(args[1]) != ".json") | ||
28 | { | ||
29 | Console.WriteLine("Output needs to be .json"); | ||
30 | return; | ||
31 | } | ||
32 | |||
33 | string prefix = null; | ||
34 | if (args.Length > 2) | ||
35 | { | ||
36 | prefix = args[2]; | ||
37 | } | ||
38 | |||
39 | var csFile = Path.Combine(Path.GetDirectoryName(args[1]), String.Concat(prefix ?? "WindowsInstaller", "TableDefinitions.cs")); | ||
40 | |||
41 | ReadXmlWriteJson(Path.GetFullPath(args[0]), Path.GetFullPath(args[1]), Path.GetFullPath(csFile), prefix); | ||
42 | } | ||
43 | else if (Path.GetExtension(args[0]) == ".json") | ||
44 | { | ||
45 | string prefix = null; | ||
46 | if (args.Length < 2) | ||
47 | { | ||
48 | Console.WriteLine("Need to specify output folder."); | ||
49 | return; | ||
50 | } | ||
51 | else if (args.Length > 2) | ||
52 | { | ||
53 | prefix = args[2]; | ||
54 | } | ||
55 | |||
56 | ReadJsonWriteCs(Path.GetFullPath(args[0]), Path.GetFullPath(args[1]), prefix); | ||
57 | } | ||
58 | } | ||
59 | |||
60 | private static void ReadXmlWriteJson(string inputPath, string outputPath, string csOutputPath, string prefix) | ||
61 | { | ||
62 | var tableDefinitions = ReadXmlWriteCs(inputPath, csOutputPath, prefix); | ||
63 | |||
64 | var array = new JsonArray(); | ||
65 | |||
66 | foreach (var tableDefinition in tableDefinitions) | ||
67 | { | ||
68 | if (tableDefinition.Symbolless) | ||
69 | { | ||
70 | continue; | ||
71 | } | ||
72 | var symbolType = tableDefinition.SymbolDefinitionName; | ||
73 | |||
74 | var fields = new JsonArray(); | ||
75 | var firstField = true; | ||
76 | |||
77 | foreach (var columnDefinition in tableDefinition.Columns) | ||
78 | { | ||
79 | if (firstField) | ||
80 | { | ||
81 | firstField = false; | ||
82 | if (tableDefinition.SymbolIdIsPrimaryKey) | ||
83 | { | ||
84 | continue; | ||
85 | } | ||
86 | } | ||
87 | |||
88 | var fieldName = columnDefinition.Name; | ||
89 | fieldName = Regex.Replace(fieldName, "^([^_]+)_([^_]*)$", x => | ||
90 | { | ||
91 | return $"{x.Groups[2].Value}{x.Groups[1].Value}Ref"; | ||
92 | }); | ||
93 | var type = columnDefinition.Type.ToString().ToLower(); | ||
94 | |||
95 | if (type == "localized") | ||
96 | { | ||
97 | type = "string"; | ||
98 | } | ||
99 | else if (type == "object") | ||
100 | { | ||
101 | type = "path"; | ||
102 | } | ||
103 | else if (columnDefinition.Type == ColumnType.Number && columnDefinition.Length == 2 && | ||
104 | columnDefinition.MinValue == 0 && columnDefinition.MaxValue == 1) | ||
105 | { | ||
106 | type = "bool"; | ||
107 | } | ||
108 | |||
109 | if (columnDefinition.Type == ColumnType.Number && columnDefinition.Nullable) | ||
110 | { | ||
111 | type += "?"; | ||
112 | } | ||
113 | |||
114 | var field = new JsonObject | ||
115 | { | ||
116 | { fieldName, type } | ||
117 | }; | ||
118 | |||
119 | fields.Add(field); | ||
120 | } | ||
121 | |||
122 | var obj = new JsonObject | ||
123 | { | ||
124 | { symbolType, fields } | ||
125 | }; | ||
126 | array.Add(obj); | ||
127 | } | ||
128 | |||
129 | array.Sort(CompareSymbolDefinitions); | ||
130 | |||
131 | var strat = new PocoJsonSerializerStrategy(); | ||
132 | var json = SimpleJson.SimpleJson.SerializeObject(array, strat); | ||
133 | |||
134 | Directory.CreateDirectory(Path.GetDirectoryName(outputPath)); | ||
135 | File.WriteAllText(outputPath, json); | ||
136 | } | ||
137 | |||
138 | private static List<WixTableDefinition> ReadXmlWriteCs(string inputPath, string outputPath, string prefix) | ||
139 | { | ||
140 | var tableDefinitions = WixTableDefinition.LoadCollection(inputPath); | ||
141 | var text = GenerateCsTableDefinitionsFileText(prefix, tableDefinitions); | ||
142 | Console.WriteLine("Writing: {0}", outputPath); | ||
143 | File.WriteAllText(outputPath, text); | ||
144 | return tableDefinitions; | ||
145 | } | ||
146 | |||
147 | private static void ReadJsonWriteCs(string inputPath, string outputFolder, string prefix) | ||
148 | { | ||
149 | var json = File.ReadAllText(inputPath); | ||
150 | var symbols = SimpleJson.SimpleJson.DeserializeObject(json) as JsonArray; | ||
151 | |||
152 | var symbolNames = new List<string>(); | ||
153 | |||
154 | foreach (var symbolDefinition in symbols.Cast<JsonObject>()) | ||
155 | { | ||
156 | var symbolName = symbolDefinition.Keys.Single(); | ||
157 | var fields = symbolDefinition.Values.Single() as JsonArray; | ||
158 | |||
159 | var list = GetFields(fields).ToList(); | ||
160 | |||
161 | symbolNames.Add(symbolName); | ||
162 | |||
163 | var text = GenerateSymbolFileText(prefix, symbolName, list); | ||
164 | |||
165 | var pathSymbol = Path.Combine(outputFolder, symbolName + "Symbol.cs"); | ||
166 | Console.WriteLine("Writing: {0}", pathSymbol); | ||
167 | File.WriteAllText(pathSymbol, text); | ||
168 | } | ||
169 | |||
170 | var content = SymbolNamesFileContent(prefix, symbolNames); | ||
171 | var pathNames = Path.Combine(outputFolder, String.Concat(prefix, "SymbolDefinitions.cs")); | ||
172 | Console.WriteLine("Writing: {0}", pathNames); | ||
173 | File.WriteAllText(pathNames, content); | ||
174 | } | ||
175 | |||
176 | private static IEnumerable<(string Name, string Type, string ClrType, string AsFunction)> GetFields(JsonArray fields) | ||
177 | { | ||
178 | foreach (var field in fields.Cast<JsonObject>()) | ||
179 | { | ||
180 | var fieldName = field.Keys.Single(); | ||
181 | var fieldType = field.Values.Single() as string; | ||
182 | |||
183 | var clrType = ConvertToClrType(fieldType); | ||
184 | fieldType = ConvertToFieldType(fieldType); | ||
185 | |||
186 | var asFunction = $"As{(clrType.Contains("?") ? "Nullable" : "")}{fieldType}()"; | ||
187 | |||
188 | yield return (Name: fieldName, Type: fieldType, ClrType: clrType, AsFunction: asFunction); | ||
189 | } | ||
190 | } | ||
191 | |||
192 | private static string GenerateCsTableDefinitionsFileText(string prefix, List<WixTableDefinition> tableDefinitions) | ||
193 | { | ||
194 | var ns = prefix ?? "Data"; | ||
195 | |||
196 | var startClassDef = String.Join(Environment.NewLine, | ||
197 | "// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.", | ||
198 | "", | ||
199 | "namespace WixToolset.{1}", | ||
200 | "{", | ||
201 | " using WixToolset.Data.WindowsInstaller;", | ||
202 | "", | ||
203 | " public static class {2}TableDefinitions", | ||
204 | " {"); | ||
205 | var startTableDef = String.Join(Environment.NewLine, | ||
206 | " public static readonly TableDefinition {1} = new TableDefinition(", | ||
207 | " \"{2}\",", | ||
208 | " {3},", | ||
209 | " new[]", | ||
210 | " {"); | ||
211 | var columnDef = | ||
212 | " new ColumnDefinition(\"{1}\", ColumnType.{2}, {3}, primaryKey: {4}, nullable: {5}, ColumnCategory.{6}"; | ||
213 | var endColumnsDef = String.Join(Environment.NewLine, | ||
214 | " },"); | ||
215 | var unrealDef = | ||
216 | " unreal: true,"; | ||
217 | var endTableDef = String.Join(Environment.NewLine, | ||
218 | " symbolIdIsPrimaryKey: {1}", | ||
219 | " );", | ||
220 | ""); | ||
221 | var startAllTablesDef = String.Join(Environment.NewLine, | ||
222 | " public static readonly TableDefinition[] All = new[]", | ||
223 | " {"); | ||
224 | var allTableDef = | ||
225 | " {1},"; | ||
226 | var endAllTablesDef = | ||
227 | " };"; | ||
228 | var endClassDef = String.Join(Environment.NewLine, | ||
229 | " }", | ||
230 | "}"); | ||
231 | |||
232 | var sb = new StringBuilder(); | ||
233 | |||
234 | sb.AppendLine(startClassDef.Replace("{1}", ns).Replace("{2}", prefix)); | ||
235 | foreach (var tableDefinition in tableDefinitions) | ||
236 | { | ||
237 | var symbolDefinition = tableDefinition.Symbolless ? "null" : $"{prefix}SymbolDefinitions.{tableDefinition.SymbolDefinitionName}"; | ||
238 | sb.AppendLine(startTableDef.Replace("{1}", tableDefinition.VariableName).Replace("{2}", tableDefinition.Name).Replace("{3}", symbolDefinition)); | ||
239 | foreach (var columnDefinition in tableDefinition.Columns) | ||
240 | { | ||
241 | sb.Append(columnDef.Replace("{1}", columnDefinition.Name).Replace("{2}", columnDefinition.Type.ToString()).Replace("{3}", columnDefinition.Length.ToString()) | ||
242 | .Replace("{4}", columnDefinition.PrimaryKey.ToString().ToLower()).Replace("{5}", columnDefinition.Nullable.ToString().ToLower()).Replace("{6}", columnDefinition.Category.ToString())); | ||
243 | if (columnDefinition.MinValue.HasValue) | ||
244 | { | ||
245 | sb.AppendFormat(", minValue: {0}", columnDefinition.MinValue.Value); | ||
246 | } | ||
247 | if (columnDefinition.MaxValue.HasValue) | ||
248 | { | ||
249 | sb.AppendFormat(", maxValue: {0}", columnDefinition.MaxValue.Value); | ||
250 | } | ||
251 | if (!String.IsNullOrEmpty(columnDefinition.KeyTable)) | ||
252 | { | ||
253 | sb.AppendFormat(", keyTable: \"{0}\"", columnDefinition.KeyTable); | ||
254 | } | ||
255 | if (columnDefinition.KeyColumn.HasValue) | ||
256 | { | ||
257 | sb.AppendFormat(", keyColumn: {0}", columnDefinition.KeyColumn.Value); | ||
258 | } | ||
259 | if (!String.IsNullOrEmpty(columnDefinition.Possibilities)) | ||
260 | { | ||
261 | sb.AppendFormat(", possibilities: \"{0}\"", columnDefinition.Possibilities); | ||
262 | } | ||
263 | if (!String.IsNullOrEmpty(columnDefinition.Description)) | ||
264 | { | ||
265 | sb.AppendFormat(", description: \"{0}\"", columnDefinition.Description.Replace("\\", "\\\\").Replace("\"", "\\\"")); | ||
266 | } | ||
267 | if (columnDefinition.ModularizeType.HasValue && columnDefinition.ModularizeType.Value != ColumnModularizeType.None) | ||
268 | { | ||
269 | sb.AppendFormat(", modularizeType: ColumnModularizeType.{0}", columnDefinition.ModularizeType.ToString()); | ||
270 | } | ||
271 | if (columnDefinition.ForceLocalizable) | ||
272 | { | ||
273 | sb.Append(", forceLocalizable: true"); | ||
274 | } | ||
275 | if (columnDefinition.UseCData) | ||
276 | { | ||
277 | sb.Append(", useCData: true"); | ||
278 | } | ||
279 | if (columnDefinition.Unreal) | ||
280 | { | ||
281 | sb.Append(", unreal: true"); | ||
282 | } | ||
283 | sb.AppendLine("),"); | ||
284 | } | ||
285 | sb.AppendLine(endColumnsDef); | ||
286 | if (tableDefinition.Unreal) | ||
287 | { | ||
288 | sb.AppendLine(unrealDef); | ||
289 | } | ||
290 | sb.AppendLine(endTableDef.Replace("{1}", tableDefinition.SymbolIdIsPrimaryKey.ToString().ToLower())); | ||
291 | } | ||
292 | sb.AppendLine(startAllTablesDef); | ||
293 | foreach (var tableDefinition in tableDefinitions) | ||
294 | { | ||
295 | sb.AppendLine(allTableDef.Replace("{1}", tableDefinition.VariableName)); | ||
296 | } | ||
297 | sb.AppendLine(endAllTablesDef); | ||
298 | sb.AppendLine(endClassDef); | ||
299 | |||
300 | return sb.ToString(); | ||
301 | } | ||
302 | |||
303 | private static string GenerateSymbolFileText(string prefix, string symbolName, List<(string Name, string Type, string ClrType, string AsFunction)> symbolFields) | ||
304 | { | ||
305 | var ns = prefix ?? "Data"; | ||
306 | var toString = String.IsNullOrEmpty(prefix) ? null : ".ToString()"; | ||
307 | |||
308 | var startFileDef = String.Join(Environment.NewLine, | ||
309 | "// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.", | ||
310 | "", | ||
311 | "namespace WixToolset.{2}", | ||
312 | "{"); | ||
313 | var usingDataDef = | ||
314 | " using WixToolset.Data;"; | ||
315 | var startSymbolDef = String.Join(Environment.NewLine, | ||
316 | " using WixToolset.{2}.Symbols;", | ||
317 | "", | ||
318 | " public static partial class {3}SymbolDefinitions", | ||
319 | " {", | ||
320 | " public static readonly IntermediateSymbolDefinition {1} = new IntermediateSymbolDefinition(", | ||
321 | " {3}SymbolDefinitionType.{1}{4},", | ||
322 | " new{5}[]", | ||
323 | " {"); | ||
324 | var fieldDef = | ||
325 | " new IntermediateFieldDefinition(nameof({1}SymbolFields.{2}), IntermediateFieldType.{3}),"; | ||
326 | var endSymbolDef = String.Join(Environment.NewLine, | ||
327 | " },", | ||
328 | " typeof({1}Symbol));", | ||
329 | " }", | ||
330 | "}", | ||
331 | "", | ||
332 | "namespace WixToolset.{2}.Symbols", | ||
333 | "{"); | ||
334 | var startEnumDef = String.Join(Environment.NewLine, | ||
335 | " public enum {1}SymbolFields", | ||
336 | " {"); | ||
337 | var fieldEnum = | ||
338 | " {2},"; | ||
339 | var startSymbol = String.Join(Environment.NewLine, | ||
340 | " }", | ||
341 | "", | ||
342 | " public class {1}Symbol : IntermediateSymbol", | ||
343 | " {", | ||
344 | " public {1}Symbol() : base({3}SymbolDefinitions.{1}, null, null)", | ||
345 | " {", | ||
346 | " }", | ||
347 | "", | ||
348 | " public {1}Symbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base({3}SymbolDefinitions.{1}, sourceLineNumber, id)", | ||
349 | " {", | ||
350 | " }", | ||
351 | "", | ||
352 | " public IntermediateField this[{1}SymbolFields index] => this.Fields[(int)index];"); | ||
353 | var fieldProp = String.Join(Environment.NewLine, | ||
354 | "", | ||
355 | " public {4} {2}", | ||
356 | " {", | ||
357 | " get => {6}this.Fields[(int){1}SymbolFields.{2}]{5};", | ||
358 | " set => this.Set((int){1}SymbolFields.{2}, value);", | ||
359 | " }"); | ||
360 | var endSymbol = String.Join(Environment.NewLine, | ||
361 | " }", | ||
362 | "}"); | ||
363 | |||
364 | var sb = new StringBuilder(); | ||
365 | |||
366 | sb.AppendLine(startFileDef.Replace("{2}", ns)); | ||
367 | if (ns != "Data") | ||
368 | { | ||
369 | sb.AppendLine(usingDataDef); | ||
370 | } | ||
371 | sb.AppendLine(startSymbolDef.Replace("{1}", symbolName).Replace("{2}", ns).Replace("{3}", prefix).Replace("{4}", toString).Replace("{5}", symbolFields.Any() ? null : " IntermediateFieldDefinition")); | ||
372 | foreach (var field in symbolFields) | ||
373 | { | ||
374 | sb.AppendLine(fieldDef.Replace("{1}", symbolName).Replace("{2}", field.Name).Replace("{3}", field.Type)); | ||
375 | } | ||
376 | sb.AppendLine(endSymbolDef.Replace("{1}", symbolName).Replace("{2}", ns).Replace("{3}", prefix)); | ||
377 | if (ns != "Data") | ||
378 | { | ||
379 | sb.AppendLine(usingDataDef); | ||
380 | sb.AppendLine(); | ||
381 | } | ||
382 | sb.AppendLine(startEnumDef.Replace("{1}", symbolName)); | ||
383 | foreach (var field in symbolFields) | ||
384 | { | ||
385 | sb.AppendLine(fieldEnum.Replace("{1}", symbolName).Replace("{2}", field.Name)); | ||
386 | } | ||
387 | sb.AppendLine(startSymbol.Replace("{1}", symbolName).Replace("{2}", ns).Replace("{3}", prefix)); | ||
388 | foreach (var field in symbolFields) | ||
389 | { | ||
390 | var useCast = ns == "Data" && field.AsFunction != "AsPath()"; | ||
391 | var cast = useCast ? $"({field.ClrType})" : null; | ||
392 | var asFunction = useCast ? null : $".{field.AsFunction}"; | ||
393 | sb.AppendLine(fieldProp.Replace("{1}", symbolName).Replace("{2}", field.Name).Replace("{3}", field.Type).Replace("{4}", field.ClrType).Replace("{5}", asFunction).Replace("{6}", cast)); | ||
394 | } | ||
395 | sb.Append(endSymbol); | ||
396 | |||
397 | return sb.ToString(); | ||
398 | } | ||
399 | |||
400 | private static string SymbolNamesFileContent(string prefix, List<string> symbolNames) | ||
401 | { | ||
402 | var ns = prefix ?? "Data"; | ||
403 | |||
404 | var header = String.Join(Environment.NewLine, | ||
405 | "// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.", | ||
406 | "", | ||
407 | "namespace WixToolset.{2}", | ||
408 | "{", | ||
409 | " using System;", | ||
410 | " using WixToolset.Data;", | ||
411 | "", | ||
412 | " public enum {3}SymbolDefinitionType", | ||
413 | " {"); | ||
414 | var namesFormat = | ||
415 | " {1},"; | ||
416 | var midpoint = String.Join(Environment.NewLine, | ||
417 | " }", | ||
418 | "", | ||
419 | " public static partial class {3}SymbolDefinitions", | ||
420 | " {", | ||
421 | " public static readonly Version Version = new Version(\"4.0.0\");", | ||
422 | "", | ||
423 | " public static IntermediateSymbolDefinition ByName(string name)", | ||
424 | " {", | ||
425 | " if (!Enum.TryParse(name, out {3}SymbolDefinitionType type))", | ||
426 | " {", | ||
427 | " return null;", | ||
428 | " }", | ||
429 | "", | ||
430 | " return ByType(type);", | ||
431 | " }", | ||
432 | "", | ||
433 | " public static IntermediateSymbolDefinition ByType({3}SymbolDefinitionType type)", | ||
434 | " {", | ||
435 | " switch (type)", | ||
436 | " {"); | ||
437 | |||
438 | var caseFormat = String.Join(Environment.NewLine, | ||
439 | " case {3}SymbolDefinitionType.{1}:", | ||
440 | " return {3}SymbolDefinitions.{1};", | ||
441 | ""); | ||
442 | |||
443 | var footer = String.Join(Environment.NewLine, | ||
444 | " default:", | ||
445 | " throw new ArgumentOutOfRangeException(nameof(type));", | ||
446 | " }", | ||
447 | " }", | ||
448 | " }", | ||
449 | "}"); | ||
450 | |||
451 | var sb = new StringBuilder(); | ||
452 | |||
453 | sb.AppendLine(header.Replace("{2}", ns).Replace("{3}", prefix)); | ||
454 | foreach (var symbolName in symbolNames) | ||
455 | { | ||
456 | sb.AppendLine(namesFormat.Replace("{1}", symbolName).Replace("{2}", ns).Replace("{3}", prefix)); | ||
457 | } | ||
458 | sb.AppendLine(midpoint.Replace("{2}", ns).Replace("{3}", prefix)); | ||
459 | foreach (var symbolName in symbolNames) | ||
460 | { | ||
461 | sb.AppendLine(caseFormat.Replace("{1}", symbolName).Replace("{2}", ns).Replace("{3}", prefix)); | ||
462 | } | ||
463 | sb.AppendLine(footer); | ||
464 | |||
465 | return sb.ToString(); | ||
466 | } | ||
467 | |||
468 | private static string ConvertToFieldType(string fieldType) | ||
469 | { | ||
470 | switch (fieldType.ToLowerInvariant()) | ||
471 | { | ||
472 | case "bool": | ||
473 | return "Bool"; | ||
474 | case "bool?": | ||
475 | return "Number"; | ||
476 | |||
477 | case "string": | ||
478 | case "preserved": | ||
479 | return "String"; | ||
480 | |||
481 | case "number": | ||
482 | case "number?": | ||
483 | return "Number"; | ||
484 | |||
485 | case "path": | ||
486 | return "Path"; | ||
487 | } | ||
488 | |||
489 | throw new ArgumentException(fieldType); | ||
490 | } | ||
491 | |||
492 | private static string ConvertToClrType(string fieldType) | ||
493 | { | ||
494 | switch (fieldType.ToLowerInvariant()) | ||
495 | { | ||
496 | case "bool": | ||
497 | return "bool"; | ||
498 | case "bool?": | ||
499 | return "bool?"; | ||
500 | |||
501 | case "string": | ||
502 | case "preserved": | ||
503 | return "string"; | ||
504 | |||
505 | case "number": | ||
506 | return "int"; | ||
507 | case "number?": | ||
508 | return "int?"; | ||
509 | |||
510 | case "path": | ||
511 | return "IntermediateFieldPathValue"; | ||
512 | } | ||
513 | |||
514 | throw new ArgumentException(fieldType); | ||
515 | } | ||
516 | |||
517 | private static int CompareSymbolDefinitions(object x, object y) | ||
518 | { | ||
519 | var first = (JsonObject)x; | ||
520 | var second = (JsonObject)y; | ||
521 | |||
522 | var firstType = first.Keys.Single(); | ||
523 | var secondType = second.Keys.Single(); | ||
524 | |||
525 | return firstType.CompareTo(secondType); | ||
526 | } | ||
527 | } | ||
528 | } | ||