diff options
author | Rob Mensching <rob@firegiant.com> | 2021-04-22 16:36:39 -0700 |
---|---|---|
committer | Rob Mensching <rob@firegiant.com> | 2021-04-22 16:50:15 -0700 |
commit | 6a24996a2e831cfe402398af65b31fb1ecd575a9 (patch) | |
tree | e377370df4bc7901745c6b5f50268fa665edb3f8 /src/internal/TablesAndTuples | |
parent | 2587a8b46b705d53156f5cd1bd866f855049081d (diff) | |
download | wix-6a24996a2e831cfe402398af65b31fb1ecd575a9.tar.gz wix-6a24996a2e831cfe402398af65b31fb1ecd575a9.tar.bz2 wix-6a24996a2e831cfe402398af65b31fb1ecd575a9.zip |
Move WixBuildTools into internal
Diffstat (limited to 'src/internal/TablesAndTuples')
-rw-r--r-- | src/internal/TablesAndTuples/ColumnDefinitionEnums.cs | 56 | ||||
-rw-r--r-- | src/internal/TablesAndTuples/Program.cs | 528 | ||||
-rw-r--r-- | src/internal/TablesAndTuples/SimpleJson.cs | 2127 | ||||
-rw-r--r-- | src/internal/TablesAndTuples/TablesAndTuples.csproj | 8 | ||||
-rw-r--r-- | src/internal/TablesAndTuples/WixColumnDefinition.cs | 296 | ||||
-rw-r--r-- | src/internal/TablesAndTuples/WixTableDefinition.cs | 169 |
6 files changed, 3184 insertions, 0 deletions
diff --git a/src/internal/TablesAndTuples/ColumnDefinitionEnums.cs b/src/internal/TablesAndTuples/ColumnDefinitionEnums.cs new file mode 100644 index 00000000..1499500e --- /dev/null +++ b/src/internal/TablesAndTuples/ColumnDefinitionEnums.cs | |||
@@ -0,0 +1,56 @@ | |||
1 | namespace TablesAndSymbols | ||
2 | { | ||
3 | public enum ColumnCategory | ||
4 | { | ||
5 | Unknown, | ||
6 | Text, | ||
7 | UpperCase, | ||
8 | LowerCase, | ||
9 | Integer, | ||
10 | DoubleInteger, | ||
11 | TimeDate, | ||
12 | Identifier, | ||
13 | Property, | ||
14 | Filename, | ||
15 | WildCardFilename, | ||
16 | Path, | ||
17 | Paths, | ||
18 | AnyPath, | ||
19 | DefaultDir, | ||
20 | RegPath, | ||
21 | Formatted, | ||
22 | Template, | ||
23 | Condition, | ||
24 | Guid, | ||
25 | Version, | ||
26 | Language, | ||
27 | Binary, | ||
28 | CustomSource, | ||
29 | Cabinet, | ||
30 | Shortcut, | ||
31 | FormattedSDDLText, | ||
32 | } | ||
33 | |||
34 | public enum ColumnModularizeType | ||
35 | { | ||
36 | None, | ||
37 | Column, | ||
38 | Icon, | ||
39 | CompanionFile, | ||
40 | Condition, | ||
41 | ControlEventArgument, | ||
42 | ControlText, | ||
43 | Property, | ||
44 | SemicolonDelimited, | ||
45 | } | ||
46 | |||
47 | public enum ColumnType | ||
48 | { | ||
49 | Unknown, | ||
50 | String, | ||
51 | Localized, | ||
52 | Number, | ||
53 | Object, | ||
54 | Preserved, | ||
55 | } | ||
56 | } | ||
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 | } | ||
diff --git a/src/internal/TablesAndTuples/SimpleJson.cs b/src/internal/TablesAndTuples/SimpleJson.cs new file mode 100644 index 00000000..3d956f6e --- /dev/null +++ b/src/internal/TablesAndTuples/SimpleJson.cs | |||
@@ -0,0 +1,2127 @@ | |||
1 | //----------------------------------------------------------------------- | ||
2 | // <copyright file="SimpleJson.cs" company="The Outercurve Foundation"> | ||
3 | // Copyright (c) 2011, The Outercurve Foundation. | ||
4 | // | ||
5 | // Licensed under the MIT License (the "License"); | ||
6 | // you may not use this file except in compliance with the License. | ||
7 | // You may obtain a copy of the License at | ||
8 | // http://www.opensource.org/licenses/mit-license.php | ||
9 | // | ||
10 | // Unless required by applicable law or agreed to in writing, software | ||
11 | // distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | // See the License for the specific language governing permissions and | ||
14 | // limitations under the License. | ||
15 | // </copyright> | ||
16 | // <author>Nathan Totten (ntotten.com), Jim Zimmerman (jimzimmerman.com) and Prabir Shrestha (prabir.me)</author> | ||
17 | // <website>https://github.com/facebook-csharp-sdk/simple-json</website> | ||
18 | //----------------------------------------------------------------------- | ||
19 | |||
20 | // VERSION: | ||
21 | |||
22 | // NOTE: uncomment the following line to make SimpleJson class internal. | ||
23 | #define SIMPLE_JSON_INTERNAL | ||
24 | |||
25 | // NOTE: uncomment the following line to make JsonArray and JsonObject class internal. | ||
26 | #define SIMPLE_JSON_OBJARRAYINTERNAL | ||
27 | |||
28 | // NOTE: uncomment the following line to enable dynamic support. | ||
29 | //#define SIMPLE_JSON_DYNAMIC | ||
30 | |||
31 | // NOTE: uncomment the following line to enable DataContract support. | ||
32 | //#define SIMPLE_JSON_DATACONTRACT | ||
33 | |||
34 | // NOTE: uncomment the following line to enable IReadOnlyCollection<T> and IReadOnlyList<T> support. | ||
35 | //#define SIMPLE_JSON_READONLY_COLLECTIONS | ||
36 | |||
37 | // NOTE: uncomment the following line to disable linq expressions/compiled lambda (better performance) instead of method.invoke(). | ||
38 | // define if you are using .net framework <= 3.0 or < WP7.5 | ||
39 | //#define SIMPLE_JSON_NO_LINQ_EXPRESSION | ||
40 | |||
41 | // NOTE: uncomment the following line if you are compiling under Window Metro style application/library. | ||
42 | // usually already defined in properties | ||
43 | //#define NETFX_CORE; | ||
44 | |||
45 | // If you are targetting WinStore, WP8 and NET4.5+ PCL make sure to #define SIMPLE_JSON_TYPEINFO; | ||
46 | |||
47 | // original json parsing code from http://techblog.procurios.nl/k/618/news/view/14605/14863/How-do-I-write-my-own-parser-for-JSON.html | ||
48 | |||
49 | #if NETFX_CORE | ||
50 | #define SIMPLE_JSON_TYPEINFO | ||
51 | #endif | ||
52 | |||
53 | using System; | ||
54 | using System.CodeDom.Compiler; | ||
55 | using System.Collections; | ||
56 | using System.Collections.Generic; | ||
57 | #if !SIMPLE_JSON_NO_LINQ_EXPRESSION | ||
58 | using System.Linq.Expressions; | ||
59 | #endif | ||
60 | using System.ComponentModel; | ||
61 | using System.Diagnostics.CodeAnalysis; | ||
62 | #if SIMPLE_JSON_DYNAMIC | ||
63 | using System.Dynamic; | ||
64 | #endif | ||
65 | using System.Globalization; | ||
66 | using System.Reflection; | ||
67 | using System.Runtime.Serialization; | ||
68 | using System.Text; | ||
69 | using SimpleJson.Reflection; | ||
70 | |||
71 | // ReSharper disable LoopCanBeConvertedToQuery | ||
72 | // ReSharper disable RedundantExplicitArrayCreation | ||
73 | // ReSharper disable SuggestUseVarKeywordEvident | ||
74 | namespace SimpleJson | ||
75 | { | ||
76 | /// <summary> | ||
77 | /// Represents the json array. | ||
78 | /// </summary> | ||
79 | [GeneratedCode("simple-json", "1.0.0")] | ||
80 | [EditorBrowsable(EditorBrowsableState.Never)] | ||
81 | [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] | ||
82 | #if SIMPLE_JSON_OBJARRAYINTERNAL | ||
83 | internal | ||
84 | #else | ||
85 | public | ||
86 | #endif | ||
87 | class JsonArray : List<object> | ||
88 | { | ||
89 | /// <summary> | ||
90 | /// Initializes a new instance of the <see cref="JsonArray"/> class. | ||
91 | /// </summary> | ||
92 | public JsonArray() { } | ||
93 | |||
94 | /// <summary> | ||
95 | /// Initializes a new instance of the <see cref="JsonArray"/> class. | ||
96 | /// </summary> | ||
97 | /// <param name="capacity">The capacity of the json array.</param> | ||
98 | public JsonArray(int capacity) : base(capacity) { } | ||
99 | |||
100 | /// <summary> | ||
101 | /// The json representation of the array. | ||
102 | /// </summary> | ||
103 | /// <returns>The json representation of the array.</returns> | ||
104 | public override string ToString() | ||
105 | { | ||
106 | return SimpleJson.SerializeObject(this) ?? string.Empty; | ||
107 | } | ||
108 | } | ||
109 | |||
110 | /// <summary> | ||
111 | /// Represents the json object. | ||
112 | /// </summary> | ||
113 | [GeneratedCode("simple-json", "1.0.0")] | ||
114 | [EditorBrowsable(EditorBrowsableState.Never)] | ||
115 | [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] | ||
116 | #if SIMPLE_JSON_OBJARRAYINTERNAL | ||
117 | internal | ||
118 | #else | ||
119 | public | ||
120 | #endif | ||
121 | class JsonObject : | ||
122 | #if SIMPLE_JSON_DYNAMIC | ||
123 | DynamicObject, | ||
124 | #endif | ||
125 | IDictionary<string, object> | ||
126 | { | ||
127 | /// <summary> | ||
128 | /// The internal member dictionary. | ||
129 | /// </summary> | ||
130 | private readonly Dictionary<string, object> _members; | ||
131 | |||
132 | /// <summary> | ||
133 | /// Initializes a new instance of <see cref="JsonObject"/>. | ||
134 | /// </summary> | ||
135 | public JsonObject() | ||
136 | { | ||
137 | _members = new Dictionary<string, object>(); | ||
138 | } | ||
139 | |||
140 | /// <summary> | ||
141 | /// Initializes a new instance of <see cref="JsonObject"/>. | ||
142 | /// </summary> | ||
143 | /// <param name="comparer">The <see cref="T:System.Collections.Generic.IEqualityComparer`1"/> implementation to use when comparing keys, or null to use the default <see cref="T:System.Collections.Generic.EqualityComparer`1"/> for the type of the key.</param> | ||
144 | public JsonObject(IEqualityComparer<string> comparer) | ||
145 | { | ||
146 | _members = new Dictionary<string, object>(comparer); | ||
147 | } | ||
148 | |||
149 | /// <summary> | ||
150 | /// Gets the <see cref="System.Object"/> at the specified index. | ||
151 | /// </summary> | ||
152 | /// <value></value> | ||
153 | public object this[int index] | ||
154 | { | ||
155 | get { return GetAtIndex(_members, index); } | ||
156 | } | ||
157 | |||
158 | internal static object GetAtIndex(IDictionary<string, object> obj, int index) | ||
159 | { | ||
160 | if (obj == null) | ||
161 | throw new ArgumentNullException("obj"); | ||
162 | if (index >= obj.Count) | ||
163 | throw new ArgumentOutOfRangeException("index"); | ||
164 | int i = 0; | ||
165 | foreach (KeyValuePair<string, object> o in obj) | ||
166 | if (i++ == index) return o.Value; | ||
167 | return null; | ||
168 | } | ||
169 | |||
170 | /// <summary> | ||
171 | /// Adds the specified key. | ||
172 | /// </summary> | ||
173 | /// <param name="key">The key.</param> | ||
174 | /// <param name="value">The value.</param> | ||
175 | public void Add(string key, object value) | ||
176 | { | ||
177 | _members.Add(key, value); | ||
178 | } | ||
179 | |||
180 | /// <summary> | ||
181 | /// Determines whether the specified key contains key. | ||
182 | /// </summary> | ||
183 | /// <param name="key">The key.</param> | ||
184 | /// <returns> | ||
185 | /// <c>true</c> if the specified key contains key; otherwise, <c>false</c>. | ||
186 | /// </returns> | ||
187 | public bool ContainsKey(string key) | ||
188 | { | ||
189 | return _members.ContainsKey(key); | ||
190 | } | ||
191 | |||
192 | /// <summary> | ||
193 | /// Gets the keys. | ||
194 | /// </summary> | ||
195 | /// <value>The keys.</value> | ||
196 | public ICollection<string> Keys | ||
197 | { | ||
198 | get { return _members.Keys; } | ||
199 | } | ||
200 | |||
201 | /// <summary> | ||
202 | /// Removes the specified key. | ||
203 | /// </summary> | ||
204 | /// <param name="key">The key.</param> | ||
205 | /// <returns></returns> | ||
206 | public bool Remove(string key) | ||
207 | { | ||
208 | return _members.Remove(key); | ||
209 | } | ||
210 | |||
211 | /// <summary> | ||
212 | /// Tries the get value. | ||
213 | /// </summary> | ||
214 | /// <param name="key">The key.</param> | ||
215 | /// <param name="value">The value.</param> | ||
216 | /// <returns></returns> | ||
217 | public bool TryGetValue(string key, out object value) | ||
218 | { | ||
219 | return _members.TryGetValue(key, out value); | ||
220 | } | ||
221 | |||
222 | /// <summary> | ||
223 | /// Gets the values. | ||
224 | /// </summary> | ||
225 | /// <value>The values.</value> | ||
226 | public ICollection<object> Values | ||
227 | { | ||
228 | get { return _members.Values; } | ||
229 | } | ||
230 | |||
231 | /// <summary> | ||
232 | /// Gets or sets the <see cref="System.Object"/> with the specified key. | ||
233 | /// </summary> | ||
234 | /// <value></value> | ||
235 | public object this[string key] | ||
236 | { | ||
237 | get { return _members[key]; } | ||
238 | set { _members[key] = value; } | ||
239 | } | ||
240 | |||
241 | /// <summary> | ||
242 | /// Adds the specified item. | ||
243 | /// </summary> | ||
244 | /// <param name="item">The item.</param> | ||
245 | public void Add(KeyValuePair<string, object> item) | ||
246 | { | ||
247 | _members.Add(item.Key, item.Value); | ||
248 | } | ||
249 | |||
250 | /// <summary> | ||
251 | /// Clears this instance. | ||
252 | /// </summary> | ||
253 | public void Clear() | ||
254 | { | ||
255 | _members.Clear(); | ||
256 | } | ||
257 | |||
258 | /// <summary> | ||
259 | /// Determines whether [contains] [the specified item]. | ||
260 | /// </summary> | ||
261 | /// <param name="item">The item.</param> | ||
262 | /// <returns> | ||
263 | /// <c>true</c> if [contains] [the specified item]; otherwise, <c>false</c>. | ||
264 | /// </returns> | ||
265 | public bool Contains(KeyValuePair<string, object> item) | ||
266 | { | ||
267 | return _members.ContainsKey(item.Key) && _members[item.Key] == item.Value; | ||
268 | } | ||
269 | |||
270 | /// <summary> | ||
271 | /// Copies to. | ||
272 | /// </summary> | ||
273 | /// <param name="array">The array.</param> | ||
274 | /// <param name="arrayIndex">Index of the array.</param> | ||
275 | public void CopyTo(KeyValuePair<string, object>[] array, int arrayIndex) | ||
276 | { | ||
277 | if (array == null) throw new ArgumentNullException("array"); | ||
278 | int num = Count; | ||
279 | foreach (KeyValuePair<string, object> kvp in this) | ||
280 | { | ||
281 | array[arrayIndex++] = kvp; | ||
282 | if (--num <= 0) | ||
283 | return; | ||
284 | } | ||
285 | } | ||
286 | |||
287 | /// <summary> | ||
288 | /// Gets the count. | ||
289 | /// </summary> | ||
290 | /// <value>The count.</value> | ||
291 | public int Count | ||
292 | { | ||
293 | get { return _members.Count; } | ||
294 | } | ||
295 | |||
296 | /// <summary> | ||
297 | /// Gets a value indicating whether this instance is read only. | ||
298 | /// </summary> | ||
299 | /// <value> | ||
300 | /// <c>true</c> if this instance is read only; otherwise, <c>false</c>. | ||
301 | /// </value> | ||
302 | public bool IsReadOnly | ||
303 | { | ||
304 | get { return false; } | ||
305 | } | ||
306 | |||
307 | /// <summary> | ||
308 | /// Removes the specified item. | ||
309 | /// </summary> | ||
310 | /// <param name="item">The item.</param> | ||
311 | /// <returns></returns> | ||
312 | public bool Remove(KeyValuePair<string, object> item) | ||
313 | { | ||
314 | return _members.Remove(item.Key); | ||
315 | } | ||
316 | |||
317 | /// <summary> | ||
318 | /// Gets the enumerator. | ||
319 | /// </summary> | ||
320 | /// <returns></returns> | ||
321 | public IEnumerator<KeyValuePair<string, object>> GetEnumerator() | ||
322 | { | ||
323 | return _members.GetEnumerator(); | ||
324 | } | ||
325 | |||
326 | /// <summary> | ||
327 | /// Returns an enumerator that iterates through a collection. | ||
328 | /// </summary> | ||
329 | /// <returns> | ||
330 | /// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection. | ||
331 | /// </returns> | ||
332 | IEnumerator IEnumerable.GetEnumerator() | ||
333 | { | ||
334 | return _members.GetEnumerator(); | ||
335 | } | ||
336 | |||
337 | /// <summary> | ||
338 | /// Returns a json <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>. | ||
339 | /// </summary> | ||
340 | /// <returns> | ||
341 | /// A json <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>. | ||
342 | /// </returns> | ||
343 | public override string ToString() | ||
344 | { | ||
345 | return SimpleJson.SerializeObject(this); | ||
346 | } | ||
347 | |||
348 | #if SIMPLE_JSON_DYNAMIC | ||
349 | /// <summary> | ||
350 | /// Provides implementation for type conversion operations. Classes derived from the <see cref="T:System.Dynamic.DynamicObject"/> class can override this method to specify dynamic behavior for operations that convert an object from one type to another. | ||
351 | /// </summary> | ||
352 | /// <param name="binder">Provides information about the conversion operation. The binder.Type property provides the type to which the object must be converted. For example, for the statement (String)sampleObject in C# (CType(sampleObject, Type) in Visual Basic), where sampleObject is an instance of the class derived from the <see cref="T:System.Dynamic.DynamicObject"/> class, binder.Type returns the <see cref="T:System.String"/> type. The binder.Explicit property provides information about the kind of conversion that occurs. It returns true for explicit conversion and false for implicit conversion.</param> | ||
353 | /// <param name="result">The result of the type conversion operation.</param> | ||
354 | /// <returns> | ||
355 | /// Alwasy returns true. | ||
356 | /// </returns> | ||
357 | public override bool TryConvert(ConvertBinder binder, out object result) | ||
358 | { | ||
359 | // <pex> | ||
360 | if (binder == null) | ||
361 | throw new ArgumentNullException("binder"); | ||
362 | // </pex> | ||
363 | Type targetType = binder.Type; | ||
364 | |||
365 | if ((targetType == typeof(IEnumerable)) || | ||
366 | (targetType == typeof(IEnumerable<KeyValuePair<string, object>>)) || | ||
367 | (targetType == typeof(IDictionary<string, object>)) || | ||
368 | (targetType == typeof(IDictionary))) | ||
369 | { | ||
370 | result = this; | ||
371 | return true; | ||
372 | } | ||
373 | |||
374 | return base.TryConvert(binder, out result); | ||
375 | } | ||
376 | |||
377 | /// <summary> | ||
378 | /// Provides the implementation for operations that delete an object member. This method is not intended for use in C# or Visual Basic. | ||
379 | /// </summary> | ||
380 | /// <param name="binder">Provides information about the deletion.</param> | ||
381 | /// <returns> | ||
382 | /// Alwasy returns true. | ||
383 | /// </returns> | ||
384 | public override bool TryDeleteMember(DeleteMemberBinder binder) | ||
385 | { | ||
386 | // <pex> | ||
387 | if (binder == null) | ||
388 | throw new ArgumentNullException("binder"); | ||
389 | // </pex> | ||
390 | return _members.Remove(binder.Name); | ||
391 | } | ||
392 | |||
393 | /// <summary> | ||
394 | /// Provides the implementation for operations that get a value by index. Classes derived from the <see cref="T:System.Dynamic.DynamicObject"/> class can override this method to specify dynamic behavior for indexing operations. | ||
395 | /// </summary> | ||
396 | /// <param name="binder">Provides information about the operation.</param> | ||
397 | /// <param name="indexes">The indexes that are used in the operation. For example, for the sampleObject[3] operation in C# (sampleObject(3) in Visual Basic), where sampleObject is derived from the DynamicObject class, <paramref name="indexes"/> is equal to 3.</param> | ||
398 | /// <param name="result">The result of the index operation.</param> | ||
399 | /// <returns> | ||
400 | /// Alwasy returns true. | ||
401 | /// </returns> | ||
402 | public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) | ||
403 | { | ||
404 | if (indexes == null) throw new ArgumentNullException("indexes"); | ||
405 | if (indexes.Length == 1) | ||
406 | { | ||
407 | result = ((IDictionary<string, object>)this)[(string)indexes[0]]; | ||
408 | return true; | ||
409 | } | ||
410 | result = null; | ||
411 | return true; | ||
412 | } | ||
413 | |||
414 | /// <summary> | ||
415 | /// Provides the implementation for operations that get member values. Classes derived from the <see cref="T:System.Dynamic.DynamicObject"/> class can override this method to specify dynamic behavior for operations such as getting a value for a property. | ||
416 | /// </summary> | ||
417 | /// <param name="binder">Provides information about the object that called the dynamic operation. The binder.Name property provides the name of the member on which the dynamic operation is performed. For example, for the Console.WriteLine(sampleObject.SampleProperty) statement, where sampleObject is an instance of the class derived from the <see cref="T:System.Dynamic.DynamicObject"/> class, binder.Name returns "SampleProperty". The binder.IgnoreCase property specifies whether the member name is case-sensitive.</param> | ||
418 | /// <param name="result">The result of the get operation. For example, if the method is called for a property, you can assign the property value to <paramref name="result"/>.</param> | ||
419 | /// <returns> | ||
420 | /// Alwasy returns true. | ||
421 | /// </returns> | ||
422 | public override bool TryGetMember(GetMemberBinder binder, out object result) | ||
423 | { | ||
424 | object value; | ||
425 | if (_members.TryGetValue(binder.Name, out value)) | ||
426 | { | ||
427 | result = value; | ||
428 | return true; | ||
429 | } | ||
430 | result = null; | ||
431 | return true; | ||
432 | } | ||
433 | |||
434 | /// <summary> | ||
435 | /// Provides the implementation for operations that set a value by index. Classes derived from the <see cref="T:System.Dynamic.DynamicObject"/> class can override this method to specify dynamic behavior for operations that access objects by a specified index. | ||
436 | /// </summary> | ||
437 | /// <param name="binder">Provides information about the operation.</param> | ||
438 | /// <param name="indexes">The indexes that are used in the operation. For example, for the sampleObject[3] = 10 operation in C# (sampleObject(3) = 10 in Visual Basic), where sampleObject is derived from the <see cref="T:System.Dynamic.DynamicObject"/> class, <paramref name="indexes"/> is equal to 3.</param> | ||
439 | /// <param name="value">The value to set to the object that has the specified index. For example, for the sampleObject[3] = 10 operation in C# (sampleObject(3) = 10 in Visual Basic), where sampleObject is derived from the <see cref="T:System.Dynamic.DynamicObject"/> class, <paramref name="value"/> is equal to 10.</param> | ||
440 | /// <returns> | ||
441 | /// true if the operation is successful; otherwise, false. If this method returns false, the run-time binder of the language determines the behavior. (In most cases, a language-specific run-time exception is thrown. | ||
442 | /// </returns> | ||
443 | public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value) | ||
444 | { | ||
445 | if (indexes == null) throw new ArgumentNullException("indexes"); | ||
446 | if (indexes.Length == 1) | ||
447 | { | ||
448 | ((IDictionary<string, object>)this)[(string)indexes[0]] = value; | ||
449 | return true; | ||
450 | } | ||
451 | return base.TrySetIndex(binder, indexes, value); | ||
452 | } | ||
453 | |||
454 | /// <summary> | ||
455 | /// Provides the implementation for operations that set member values. Classes derived from the <see cref="T:System.Dynamic.DynamicObject"/> class can override this method to specify dynamic behavior for operations such as setting a value for a property. | ||
456 | /// </summary> | ||
457 | /// <param name="binder">Provides information about the object that called the dynamic operation. The binder.Name property provides the name of the member to which the value is being assigned. For example, for the statement sampleObject.SampleProperty = "Test", where sampleObject is an instance of the class derived from the <see cref="T:System.Dynamic.DynamicObject"/> class, binder.Name returns "SampleProperty". The binder.IgnoreCase property specifies whether the member name is case-sensitive.</param> | ||
458 | /// <param name="value">The value to set to the member. For example, for sampleObject.SampleProperty = "Test", where sampleObject is an instance of the class derived from the <see cref="T:System.Dynamic.DynamicObject"/> class, the <paramref name="value"/> is "Test".</param> | ||
459 | /// <returns> | ||
460 | /// true if the operation is successful; otherwise, false. If this method returns false, the run-time binder of the language determines the behavior. (In most cases, a language-specific run-time exception is thrown.) | ||
461 | /// </returns> | ||
462 | public override bool TrySetMember(SetMemberBinder binder, object value) | ||
463 | { | ||
464 | // <pex> | ||
465 | if (binder == null) | ||
466 | throw new ArgumentNullException("binder"); | ||
467 | // </pex> | ||
468 | _members[binder.Name] = value; | ||
469 | return true; | ||
470 | } | ||
471 | |||
472 | /// <summary> | ||
473 | /// Returns the enumeration of all dynamic member names. | ||
474 | /// </summary> | ||
475 | /// <returns> | ||
476 | /// A sequence that contains dynamic member names. | ||
477 | /// </returns> | ||
478 | public override IEnumerable<string> GetDynamicMemberNames() | ||
479 | { | ||
480 | foreach (var key in Keys) | ||
481 | yield return key; | ||
482 | } | ||
483 | #endif | ||
484 | } | ||
485 | } | ||
486 | |||
487 | namespace SimpleJson | ||
488 | { | ||
489 | /// <summary> | ||
490 | /// This class encodes and decodes JSON strings. | ||
491 | /// Spec. details, see http://www.json.org/ | ||
492 | /// | ||
493 | /// JSON uses Arrays and Objects. These correspond here to the datatypes JsonArray(IList<object>) and JsonObject(IDictionary<string,object>). | ||
494 | /// All numbers are parsed to doubles. | ||
495 | /// </summary> | ||
496 | [GeneratedCode("simple-json", "1.0.0")] | ||
497 | #if SIMPLE_JSON_INTERNAL | ||
498 | internal | ||
499 | #else | ||
500 | public | ||
501 | #endif | ||
502 | static class SimpleJson | ||
503 | { | ||
504 | private const int TOKEN_NONE = 0; | ||
505 | private const int TOKEN_CURLY_OPEN = 1; | ||
506 | private const int TOKEN_CURLY_CLOSE = 2; | ||
507 | private const int TOKEN_SQUARED_OPEN = 3; | ||
508 | private const int TOKEN_SQUARED_CLOSE = 4; | ||
509 | private const int TOKEN_COLON = 5; | ||
510 | private const int TOKEN_COMMA = 6; | ||
511 | private const int TOKEN_STRING = 7; | ||
512 | private const int TOKEN_NUMBER = 8; | ||
513 | private const int TOKEN_TRUE = 9; | ||
514 | private const int TOKEN_FALSE = 10; | ||
515 | private const int TOKEN_NULL = 11; | ||
516 | private const int BUILDER_CAPACITY = 2000; | ||
517 | |||
518 | private static readonly char[] EscapeTable; | ||
519 | private static readonly char[] EscapeCharacters = new char[] { '"', '\\', '\b', '\f', '\n', '\r', '\t' }; | ||
520 | private static readonly string EscapeCharactersString = new string(EscapeCharacters); | ||
521 | |||
522 | static SimpleJson() | ||
523 | { | ||
524 | EscapeTable = new char[93]; | ||
525 | EscapeTable['"'] = '"'; | ||
526 | EscapeTable['\\'] = '\\'; | ||
527 | EscapeTable['\b'] = 'b'; | ||
528 | EscapeTable['\f'] = 'f'; | ||
529 | EscapeTable['\n'] = 'n'; | ||
530 | EscapeTable['\r'] = 'r'; | ||
531 | EscapeTable['\t'] = 't'; | ||
532 | } | ||
533 | |||
534 | /// <summary> | ||
535 | /// Parses the string json into a value | ||
536 | /// </summary> | ||
537 | /// <param name="json">A JSON string.</param> | ||
538 | /// <returns>An IList<object>, a IDictionary<string,object>, a double, a string, null, true, or false</returns> | ||
539 | public static object DeserializeObject(string json) | ||
540 | { | ||
541 | object obj; | ||
542 | if (TryDeserializeObject(json, out obj)) | ||
543 | return obj; | ||
544 | throw new SerializationException("Invalid JSON string"); | ||
545 | } | ||
546 | |||
547 | /// <summary> | ||
548 | /// Try parsing the json string into a value. | ||
549 | /// </summary> | ||
550 | /// <param name="json"> | ||
551 | /// A JSON string. | ||
552 | /// </param> | ||
553 | /// <param name="obj"> | ||
554 | /// The object. | ||
555 | /// </param> | ||
556 | /// <returns> | ||
557 | /// Returns true if successfull otherwise false. | ||
558 | /// </returns> | ||
559 | [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification = "Need to support .NET 2")] | ||
560 | public static bool TryDeserializeObject(string json, out object obj) | ||
561 | { | ||
562 | bool success = true; | ||
563 | if (json != null) | ||
564 | { | ||
565 | char[] charArray = json.ToCharArray(); | ||
566 | int index = 0; | ||
567 | obj = ParseValue(charArray, ref index, ref success); | ||
568 | } | ||
569 | else | ||
570 | obj = null; | ||
571 | |||
572 | return success; | ||
573 | } | ||
574 | |||
575 | public static object DeserializeObject(string json, Type type, IJsonSerializerStrategy jsonSerializerStrategy) | ||
576 | { | ||
577 | object jsonObject = DeserializeObject(json); | ||
578 | return type == null || jsonObject != null && ReflectionUtils.IsAssignableFrom(jsonObject.GetType(), type) | ||
579 | ? jsonObject | ||
580 | : (jsonSerializerStrategy ?? CurrentJsonSerializerStrategy).DeserializeObject(jsonObject, type); | ||
581 | } | ||
582 | |||
583 | public static object DeserializeObject(string json, Type type) | ||
584 | { | ||
585 | return DeserializeObject(json, type, null); | ||
586 | } | ||
587 | |||
588 | public static T DeserializeObject<T>(string json, IJsonSerializerStrategy jsonSerializerStrategy) | ||
589 | { | ||
590 | return (T)DeserializeObject(json, typeof(T), jsonSerializerStrategy); | ||
591 | } | ||
592 | |||
593 | public static T DeserializeObject<T>(string json) | ||
594 | { | ||
595 | return (T)DeserializeObject(json, typeof(T), null); | ||
596 | } | ||
597 | |||
598 | /// <summary> | ||
599 | /// Converts a IDictionary<string,object> / IList<object> object into a JSON string | ||
600 | /// </summary> | ||
601 | /// <param name="json">A IDictionary<string,object> / IList<object></param> | ||
602 | /// <param name="jsonSerializerStrategy">Serializer strategy to use</param> | ||
603 | /// <returns>A JSON encoded string, or null if object 'json' is not serializable</returns> | ||
604 | public static string SerializeObject(object json, IJsonSerializerStrategy jsonSerializerStrategy) | ||
605 | { | ||
606 | StringBuilder builder = new StringBuilder(BUILDER_CAPACITY); | ||
607 | bool success = SerializeValue(jsonSerializerStrategy, json, builder); | ||
608 | return (success ? builder.ToString() : null); | ||
609 | } | ||
610 | |||
611 | public static string SerializeObject(object json) | ||
612 | { | ||
613 | return SerializeObject(json, CurrentJsonSerializerStrategy); | ||
614 | } | ||
615 | |||
616 | public static string EscapeToJavascriptString(string jsonString) | ||
617 | { | ||
618 | if (string.IsNullOrEmpty(jsonString)) | ||
619 | return jsonString; | ||
620 | |||
621 | StringBuilder sb = new StringBuilder(); | ||
622 | char c; | ||
623 | |||
624 | for (int i = 0; i < jsonString.Length;) | ||
625 | { | ||
626 | c = jsonString[i++]; | ||
627 | |||
628 | if (c == '\\') | ||
629 | { | ||
630 | int remainingLength = jsonString.Length - i; | ||
631 | if (remainingLength >= 2) | ||
632 | { | ||
633 | char lookahead = jsonString[i]; | ||
634 | if (lookahead == '\\') | ||
635 | { | ||
636 | sb.Append('\\'); | ||
637 | ++i; | ||
638 | } | ||
639 | else if (lookahead == '"') | ||
640 | { | ||
641 | sb.Append("\""); | ||
642 | ++i; | ||
643 | } | ||
644 | else if (lookahead == 't') | ||
645 | { | ||
646 | sb.Append('\t'); | ||
647 | ++i; | ||
648 | } | ||
649 | else if (lookahead == 'b') | ||
650 | { | ||
651 | sb.Append('\b'); | ||
652 | ++i; | ||
653 | } | ||
654 | else if (lookahead == 'n') | ||
655 | { | ||
656 | sb.Append('\n'); | ||
657 | ++i; | ||
658 | } | ||
659 | else if (lookahead == 'r') | ||
660 | { | ||
661 | sb.Append('\r'); | ||
662 | ++i; | ||
663 | } | ||
664 | } | ||
665 | } | ||
666 | else | ||
667 | { | ||
668 | sb.Append(c); | ||
669 | } | ||
670 | } | ||
671 | return sb.ToString(); | ||
672 | } | ||
673 | |||
674 | static IDictionary<string, object> ParseObject(char[] json, ref int index, ref bool success) | ||
675 | { | ||
676 | IDictionary<string, object> table = new JsonObject(); | ||
677 | int token; | ||
678 | |||
679 | // { | ||
680 | NextToken(json, ref index); | ||
681 | |||
682 | bool done = false; | ||
683 | while (!done) | ||
684 | { | ||
685 | token = LookAhead(json, index); | ||
686 | if (token == TOKEN_NONE) | ||
687 | { | ||
688 | success = false; | ||
689 | return null; | ||
690 | } | ||
691 | else if (token == TOKEN_COMMA) | ||
692 | NextToken(json, ref index); | ||
693 | else if (token == TOKEN_CURLY_CLOSE) | ||
694 | { | ||
695 | NextToken(json, ref index); | ||
696 | return table; | ||
697 | } | ||
698 | else | ||
699 | { | ||
700 | // name | ||
701 | string name = ParseString(json, ref index, ref success); | ||
702 | if (!success) | ||
703 | { | ||
704 | success = false; | ||
705 | return null; | ||
706 | } | ||
707 | // : | ||
708 | token = NextToken(json, ref index); | ||
709 | if (token != TOKEN_COLON) | ||
710 | { | ||
711 | success = false; | ||
712 | return null; | ||
713 | } | ||
714 | // value | ||
715 | object value = ParseValue(json, ref index, ref success); | ||
716 | if (!success) | ||
717 | { | ||
718 | success = false; | ||
719 | return null; | ||
720 | } | ||
721 | table[name] = value; | ||
722 | } | ||
723 | } | ||
724 | return table; | ||
725 | } | ||
726 | |||
727 | static JsonArray ParseArray(char[] json, ref int index, ref bool success) | ||
728 | { | ||
729 | JsonArray array = new JsonArray(); | ||
730 | |||
731 | // [ | ||
732 | NextToken(json, ref index); | ||
733 | |||
734 | bool done = false; | ||
735 | while (!done) | ||
736 | { | ||
737 | int token = LookAhead(json, index); | ||
738 | if (token == TOKEN_NONE) | ||
739 | { | ||
740 | success = false; | ||
741 | return null; | ||
742 | } | ||
743 | else if (token == TOKEN_COMMA) | ||
744 | NextToken(json, ref index); | ||
745 | else if (token == TOKEN_SQUARED_CLOSE) | ||
746 | { | ||
747 | NextToken(json, ref index); | ||
748 | break; | ||
749 | } | ||
750 | else | ||
751 | { | ||
752 | object value = ParseValue(json, ref index, ref success); | ||
753 | if (!success) | ||
754 | return null; | ||
755 | array.Add(value); | ||
756 | } | ||
757 | } | ||
758 | return array; | ||
759 | } | ||
760 | |||
761 | static object ParseValue(char[] json, ref int index, ref bool success) | ||
762 | { | ||
763 | switch (LookAhead(json, index)) | ||
764 | { | ||
765 | case TOKEN_STRING: | ||
766 | return ParseString(json, ref index, ref success); | ||
767 | case TOKEN_NUMBER: | ||
768 | return ParseNumber(json, ref index, ref success); | ||
769 | case TOKEN_CURLY_OPEN: | ||
770 | return ParseObject(json, ref index, ref success); | ||
771 | case TOKEN_SQUARED_OPEN: | ||
772 | return ParseArray(json, ref index, ref success); | ||
773 | case TOKEN_TRUE: | ||
774 | NextToken(json, ref index); | ||
775 | return true; | ||
776 | case TOKEN_FALSE: | ||
777 | NextToken(json, ref index); | ||
778 | return false; | ||
779 | case TOKEN_NULL: | ||
780 | NextToken(json, ref index); | ||
781 | return null; | ||
782 | case TOKEN_NONE: | ||
783 | break; | ||
784 | } | ||
785 | success = false; | ||
786 | return null; | ||
787 | } | ||
788 | |||
789 | static string ParseString(char[] json, ref int index, ref bool success) | ||
790 | { | ||
791 | StringBuilder s = new StringBuilder(BUILDER_CAPACITY); | ||
792 | char c; | ||
793 | |||
794 | EatWhitespace(json, ref index); | ||
795 | |||
796 | // " | ||
797 | c = json[index++]; | ||
798 | bool complete = false; | ||
799 | while (!complete) | ||
800 | { | ||
801 | if (index == json.Length) | ||
802 | break; | ||
803 | |||
804 | c = json[index++]; | ||
805 | if (c == '"') | ||
806 | { | ||
807 | complete = true; | ||
808 | break; | ||
809 | } | ||
810 | else if (c == '\\') | ||
811 | { | ||
812 | if (index == json.Length) | ||
813 | break; | ||
814 | c = json[index++]; | ||
815 | if (c == '"') | ||
816 | s.Append('"'); | ||
817 | else if (c == '\\') | ||
818 | s.Append('\\'); | ||
819 | else if (c == '/') | ||
820 | s.Append('/'); | ||
821 | else if (c == 'b') | ||
822 | s.Append('\b'); | ||
823 | else if (c == 'f') | ||
824 | s.Append('\f'); | ||
825 | else if (c == 'n') | ||
826 | s.Append('\n'); | ||
827 | else if (c == 'r') | ||
828 | s.Append('\r'); | ||
829 | else if (c == 't') | ||
830 | s.Append('\t'); | ||
831 | else if (c == 'u') | ||
832 | { | ||
833 | int remainingLength = json.Length - index; | ||
834 | if (remainingLength >= 4) | ||
835 | { | ||
836 | // parse the 32 bit hex into an integer codepoint | ||
837 | uint codePoint; | ||
838 | if (!(success = UInt32.TryParse(new string(json, index, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out codePoint))) | ||
839 | return ""; | ||
840 | |||
841 | // convert the integer codepoint to a unicode char and add to string | ||
842 | if (0xD800 <= codePoint && codePoint <= 0xDBFF) // if high surrogate | ||
843 | { | ||
844 | index += 4; // skip 4 chars | ||
845 | remainingLength = json.Length - index; | ||
846 | if (remainingLength >= 6) | ||
847 | { | ||
848 | uint lowCodePoint; | ||
849 | if (new string(json, index, 2) == "\\u" && UInt32.TryParse(new string(json, index + 2, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out lowCodePoint)) | ||
850 | { | ||
851 | if (0xDC00 <= lowCodePoint && lowCodePoint <= 0xDFFF) // if low surrogate | ||
852 | { | ||
853 | s.Append((char)codePoint); | ||
854 | s.Append((char)lowCodePoint); | ||
855 | index += 6; // skip 6 chars | ||
856 | continue; | ||
857 | } | ||
858 | } | ||
859 | } | ||
860 | success = false; // invalid surrogate pair | ||
861 | return ""; | ||
862 | } | ||
863 | s.Append(ConvertFromUtf32((int)codePoint)); | ||
864 | // skip 4 chars | ||
865 | index += 4; | ||
866 | } | ||
867 | else | ||
868 | break; | ||
869 | } | ||
870 | } | ||
871 | else | ||
872 | s.Append(c); | ||
873 | } | ||
874 | if (!complete) | ||
875 | { | ||
876 | success = false; | ||
877 | return null; | ||
878 | } | ||
879 | return s.ToString(); | ||
880 | } | ||
881 | |||
882 | private static string ConvertFromUtf32(int utf32) | ||
883 | { | ||
884 | // http://www.java2s.com/Open-Source/CSharp/2.6.4-mono-.net-core/System/System/Char.cs.htm | ||
885 | if (utf32 < 0 || utf32 > 0x10FFFF) | ||
886 | throw new ArgumentOutOfRangeException("utf32", "The argument must be from 0 to 0x10FFFF."); | ||
887 | if (0xD800 <= utf32 && utf32 <= 0xDFFF) | ||
888 | throw new ArgumentOutOfRangeException("utf32", "The argument must not be in surrogate pair range."); | ||
889 | if (utf32 < 0x10000) | ||
890 | return new string((char)utf32, 1); | ||
891 | utf32 -= 0x10000; | ||
892 | return new string(new char[] { (char)((utf32 >> 10) + 0xD800), (char)(utf32 % 0x0400 + 0xDC00) }); | ||
893 | } | ||
894 | |||
895 | static object ParseNumber(char[] json, ref int index, ref bool success) | ||
896 | { | ||
897 | EatWhitespace(json, ref index); | ||
898 | int lastIndex = GetLastIndexOfNumber(json, index); | ||
899 | int charLength = (lastIndex - index) + 1; | ||
900 | object returnNumber; | ||
901 | string str = new string(json, index, charLength); | ||
902 | if (str.IndexOf(".", StringComparison.OrdinalIgnoreCase) != -1 || str.IndexOf("e", StringComparison.OrdinalIgnoreCase) != -1) | ||
903 | { | ||
904 | double number; | ||
905 | success = double.TryParse(new string(json, index, charLength), NumberStyles.Any, CultureInfo.InvariantCulture, out number); | ||
906 | returnNumber = number; | ||
907 | } | ||
908 | else | ||
909 | { | ||
910 | long number; | ||
911 | success = long.TryParse(new string(json, index, charLength), NumberStyles.Any, CultureInfo.InvariantCulture, out number); | ||
912 | returnNumber = number; | ||
913 | } | ||
914 | index = lastIndex + 1; | ||
915 | return returnNumber; | ||
916 | } | ||
917 | |||
918 | static int GetLastIndexOfNumber(char[] json, int index) | ||
919 | { | ||
920 | int lastIndex; | ||
921 | for (lastIndex = index; lastIndex < json.Length; lastIndex++) | ||
922 | if ("0123456789+-.eE".IndexOf(json[lastIndex]) == -1) break; | ||
923 | return lastIndex - 1; | ||
924 | } | ||
925 | |||
926 | static void EatWhitespace(char[] json, ref int index) | ||
927 | { | ||
928 | for (; index < json.Length; index++) | ||
929 | if (" \t\n\r\b\f".IndexOf(json[index]) == -1) break; | ||
930 | } | ||
931 | |||
932 | static int LookAhead(char[] json, int index) | ||
933 | { | ||
934 | int saveIndex = index; | ||
935 | return NextToken(json, ref saveIndex); | ||
936 | } | ||
937 | |||
938 | [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] | ||
939 | static int NextToken(char[] json, ref int index) | ||
940 | { | ||
941 | EatWhitespace(json, ref index); | ||
942 | if (index == json.Length) | ||
943 | return TOKEN_NONE; | ||
944 | char c = json[index]; | ||
945 | index++; | ||
946 | switch (c) | ||
947 | { | ||
948 | case '{': | ||
949 | return TOKEN_CURLY_OPEN; | ||
950 | case '}': | ||
951 | return TOKEN_CURLY_CLOSE; | ||
952 | case '[': | ||
953 | return TOKEN_SQUARED_OPEN; | ||
954 | case ']': | ||
955 | return TOKEN_SQUARED_CLOSE; | ||
956 | case ',': | ||
957 | return TOKEN_COMMA; | ||
958 | case '"': | ||
959 | return TOKEN_STRING; | ||
960 | case '0': | ||
961 | case '1': | ||
962 | case '2': | ||
963 | case '3': | ||
964 | case '4': | ||
965 | case '5': | ||
966 | case '6': | ||
967 | case '7': | ||
968 | case '8': | ||
969 | case '9': | ||
970 | case '-': | ||
971 | return TOKEN_NUMBER; | ||
972 | case ':': | ||
973 | return TOKEN_COLON; | ||
974 | } | ||
975 | index--; | ||
976 | int remainingLength = json.Length - index; | ||
977 | // false | ||
978 | if (remainingLength >= 5) | ||
979 | { | ||
980 | if (json[index] == 'f' && json[index + 1] == 'a' && json[index + 2] == 'l' && json[index + 3] == 's' && json[index + 4] == 'e') | ||
981 | { | ||
982 | index += 5; | ||
983 | return TOKEN_FALSE; | ||
984 | } | ||
985 | } | ||
986 | // true | ||
987 | if (remainingLength >= 4) | ||
988 | { | ||
989 | if (json[index] == 't' && json[index + 1] == 'r' && json[index + 2] == 'u' && json[index + 3] == 'e') | ||
990 | { | ||
991 | index += 4; | ||
992 | return TOKEN_TRUE; | ||
993 | } | ||
994 | } | ||
995 | // null | ||
996 | if (remainingLength >= 4) | ||
997 | { | ||
998 | if (json[index] == 'n' && json[index + 1] == 'u' && json[index + 2] == 'l' && json[index + 3] == 'l') | ||
999 | { | ||
1000 | index += 4; | ||
1001 | return TOKEN_NULL; | ||
1002 | } | ||
1003 | } | ||
1004 | return TOKEN_NONE; | ||
1005 | } | ||
1006 | |||
1007 | static bool SerializeValue(IJsonSerializerStrategy jsonSerializerStrategy, object value, StringBuilder builder) | ||
1008 | { | ||
1009 | bool success = true; | ||
1010 | string stringValue = value as string; | ||
1011 | if (stringValue != null) | ||
1012 | success = SerializeString(stringValue, builder); | ||
1013 | else | ||
1014 | { | ||
1015 | IDictionary<string, object> dict = value as IDictionary<string, object>; | ||
1016 | if (dict != null) | ||
1017 | { | ||
1018 | success = SerializeObject(jsonSerializerStrategy, dict.Keys, dict.Values, builder); | ||
1019 | } | ||
1020 | else | ||
1021 | { | ||
1022 | IDictionary<string, string> stringDictionary = value as IDictionary<string, string>; | ||
1023 | if (stringDictionary != null) | ||
1024 | { | ||
1025 | success = SerializeObject(jsonSerializerStrategy, stringDictionary.Keys, stringDictionary.Values, builder); | ||
1026 | } | ||
1027 | else | ||
1028 | { | ||
1029 | IEnumerable enumerableValue = value as IEnumerable; | ||
1030 | if (enumerableValue != null) | ||
1031 | success = SerializeArray(jsonSerializerStrategy, enumerableValue, builder); | ||
1032 | else if (IsNumeric(value)) | ||
1033 | success = SerializeNumber(value, builder); | ||
1034 | else if (value is bool) | ||
1035 | builder.Append((bool)value ? "true" : "false"); | ||
1036 | else if (value == null) | ||
1037 | builder.Append("null"); | ||
1038 | else | ||
1039 | { | ||
1040 | object serializedObject; | ||
1041 | success = jsonSerializerStrategy.TrySerializeNonPrimitiveObject(value, out serializedObject); | ||
1042 | if (success) | ||
1043 | SerializeValue(jsonSerializerStrategy, serializedObject, builder); | ||
1044 | } | ||
1045 | } | ||
1046 | } | ||
1047 | } | ||
1048 | return success; | ||
1049 | } | ||
1050 | |||
1051 | static bool SerializeObject(IJsonSerializerStrategy jsonSerializerStrategy, IEnumerable keys, IEnumerable values, StringBuilder builder) | ||
1052 | { | ||
1053 | builder.Append("{"); | ||
1054 | IEnumerator ke = keys.GetEnumerator(); | ||
1055 | IEnumerator ve = values.GetEnumerator(); | ||
1056 | bool first = true; | ||
1057 | while (ke.MoveNext() && ve.MoveNext()) | ||
1058 | { | ||
1059 | object key = ke.Current; | ||
1060 | object value = ve.Current; | ||
1061 | if (!first) | ||
1062 | builder.Append(","); | ||
1063 | string stringKey = key as string; | ||
1064 | if (stringKey != null) | ||
1065 | SerializeString(stringKey, builder); | ||
1066 | else | ||
1067 | if (!SerializeValue(jsonSerializerStrategy, value, builder)) return false; | ||
1068 | builder.Append(":"); | ||
1069 | if (!SerializeValue(jsonSerializerStrategy, value, builder)) | ||
1070 | return false; | ||
1071 | first = false; | ||
1072 | } | ||
1073 | builder.Append("}"); | ||
1074 | return true; | ||
1075 | } | ||
1076 | |||
1077 | static bool SerializeArray(IJsonSerializerStrategy jsonSerializerStrategy, IEnumerable anArray, StringBuilder builder) | ||
1078 | { | ||
1079 | builder.Append("["); | ||
1080 | bool first = true; | ||
1081 | foreach (object value in anArray) | ||
1082 | { | ||
1083 | if (!first) | ||
1084 | builder.Append(","); | ||
1085 | if (!SerializeValue(jsonSerializerStrategy, value, builder)) | ||
1086 | return false; | ||
1087 | first = false; | ||
1088 | } | ||
1089 | builder.Append("]"); | ||
1090 | return true; | ||
1091 | } | ||
1092 | |||
1093 | static bool SerializeString(string aString, StringBuilder builder) | ||
1094 | { | ||
1095 | // Happy path if there's nothing to be escaped. IndexOfAny is highly optimized (and unmanaged) | ||
1096 | if (aString.IndexOfAny(EscapeCharacters) == -1) | ||
1097 | { | ||
1098 | builder.Append('"'); | ||
1099 | builder.Append(aString); | ||
1100 | builder.Append('"'); | ||
1101 | |||
1102 | return true; | ||
1103 | } | ||
1104 | |||
1105 | builder.Append('"'); | ||
1106 | int safeCharacterCount = 0; | ||
1107 | char[] charArray = aString.ToCharArray(); | ||
1108 | |||
1109 | for (int i = 0; i < charArray.Length; i++) | ||
1110 | { | ||
1111 | char c = charArray[i]; | ||
1112 | |||
1113 | // Non ascii characters are fine, buffer them up and send them to the builder | ||
1114 | // in larger chunks if possible. The escape table is a 1:1 translation table | ||
1115 | // with \0 [default(char)] denoting a safe character. | ||
1116 | if (c >= EscapeTable.Length || EscapeTable[c] == default(char)) | ||
1117 | { | ||
1118 | safeCharacterCount++; | ||
1119 | } | ||
1120 | else | ||
1121 | { | ||
1122 | if (safeCharacterCount > 0) | ||
1123 | { | ||
1124 | builder.Append(charArray, i - safeCharacterCount, safeCharacterCount); | ||
1125 | safeCharacterCount = 0; | ||
1126 | } | ||
1127 | |||
1128 | builder.Append('\\'); | ||
1129 | builder.Append(EscapeTable[c]); | ||
1130 | } | ||
1131 | } | ||
1132 | |||
1133 | if (safeCharacterCount > 0) | ||
1134 | { | ||
1135 | builder.Append(charArray, charArray.Length - safeCharacterCount, safeCharacterCount); | ||
1136 | } | ||
1137 | |||
1138 | builder.Append('"'); | ||
1139 | return true; | ||
1140 | } | ||
1141 | |||
1142 | static bool SerializeNumber(object number, StringBuilder builder) | ||
1143 | { | ||
1144 | if (number is long) | ||
1145 | builder.Append(((long)number).ToString(CultureInfo.InvariantCulture)); | ||
1146 | else if (number is ulong) | ||
1147 | builder.Append(((ulong)number).ToString(CultureInfo.InvariantCulture)); | ||
1148 | else if (number is int) | ||
1149 | builder.Append(((int)number).ToString(CultureInfo.InvariantCulture)); | ||
1150 | else if (number is uint) | ||
1151 | builder.Append(((uint)number).ToString(CultureInfo.InvariantCulture)); | ||
1152 | else if (number is decimal) | ||
1153 | builder.Append(((decimal)number).ToString(CultureInfo.InvariantCulture)); | ||
1154 | else if (number is float) | ||
1155 | builder.Append(((float)number).ToString(CultureInfo.InvariantCulture)); | ||
1156 | else | ||
1157 | builder.Append(Convert.ToDouble(number, CultureInfo.InvariantCulture).ToString("r", CultureInfo.InvariantCulture)); | ||
1158 | return true; | ||
1159 | } | ||
1160 | |||
1161 | /// <summary> | ||
1162 | /// Determines if a given object is numeric in any way | ||
1163 | /// (can be integer, double, null, etc). | ||
1164 | /// </summary> | ||
1165 | static bool IsNumeric(object value) | ||
1166 | { | ||
1167 | if (value is sbyte) return true; | ||
1168 | if (value is byte) return true; | ||
1169 | if (value is short) return true; | ||
1170 | if (value is ushort) return true; | ||
1171 | if (value is int) return true; | ||
1172 | if (value is uint) return true; | ||
1173 | if (value is long) return true; | ||
1174 | if (value is ulong) return true; | ||
1175 | if (value is float) return true; | ||
1176 | if (value is double) return true; | ||
1177 | if (value is decimal) return true; | ||
1178 | return false; | ||
1179 | } | ||
1180 | |||
1181 | private static IJsonSerializerStrategy _currentJsonSerializerStrategy; | ||
1182 | public static IJsonSerializerStrategy CurrentJsonSerializerStrategy | ||
1183 | { | ||
1184 | get | ||
1185 | { | ||
1186 | return _currentJsonSerializerStrategy ?? | ||
1187 | (_currentJsonSerializerStrategy = | ||
1188 | #if SIMPLE_JSON_DATACONTRACT | ||
1189 | DataContractJsonSerializerStrategy | ||
1190 | #else | ||
1191 | PocoJsonSerializerStrategy | ||
1192 | #endif | ||
1193 | ); | ||
1194 | } | ||
1195 | set | ||
1196 | { | ||
1197 | _currentJsonSerializerStrategy = value; | ||
1198 | } | ||
1199 | } | ||
1200 | |||
1201 | private static PocoJsonSerializerStrategy _pocoJsonSerializerStrategy; | ||
1202 | [EditorBrowsable(EditorBrowsableState.Advanced)] | ||
1203 | public static PocoJsonSerializerStrategy PocoJsonSerializerStrategy | ||
1204 | { | ||
1205 | get | ||
1206 | { | ||
1207 | return _pocoJsonSerializerStrategy ?? (_pocoJsonSerializerStrategy = new PocoJsonSerializerStrategy()); | ||
1208 | } | ||
1209 | } | ||
1210 | |||
1211 | #if SIMPLE_JSON_DATACONTRACT | ||
1212 | |||
1213 | private static DataContractJsonSerializerStrategy _dataContractJsonSerializerStrategy; | ||
1214 | [System.ComponentModel.EditorBrowsable(EditorBrowsableState.Advanced)] | ||
1215 | public static DataContractJsonSerializerStrategy DataContractJsonSerializerStrategy | ||
1216 | { | ||
1217 | get | ||
1218 | { | ||
1219 | return _dataContractJsonSerializerStrategy ?? (_dataContractJsonSerializerStrategy = new DataContractJsonSerializerStrategy()); | ||
1220 | } | ||
1221 | } | ||
1222 | |||
1223 | #endif | ||
1224 | } | ||
1225 | |||
1226 | [GeneratedCode("simple-json", "1.0.0")] | ||
1227 | #if SIMPLE_JSON_INTERNAL | ||
1228 | internal | ||
1229 | #else | ||
1230 | public | ||
1231 | #endif | ||
1232 | interface IJsonSerializerStrategy | ||
1233 | { | ||
1234 | [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification = "Need to support .NET 2")] | ||
1235 | bool TrySerializeNonPrimitiveObject(object input, out object output); | ||
1236 | object DeserializeObject(object value, Type type); | ||
1237 | } | ||
1238 | |||
1239 | [GeneratedCode("simple-json", "1.0.0")] | ||
1240 | #if SIMPLE_JSON_INTERNAL | ||
1241 | internal | ||
1242 | #else | ||
1243 | public | ||
1244 | #endif | ||
1245 | class PocoJsonSerializerStrategy : IJsonSerializerStrategy | ||
1246 | { | ||
1247 | internal IDictionary<Type, ReflectionUtils.ConstructorDelegate> ConstructorCache; | ||
1248 | internal IDictionary<Type, IDictionary<string, ReflectionUtils.GetDelegate>> GetCache; | ||
1249 | internal IDictionary<Type, IDictionary<string, KeyValuePair<Type, ReflectionUtils.SetDelegate>>> SetCache; | ||
1250 | |||
1251 | internal static readonly Type[] EmptyTypes = new Type[0]; | ||
1252 | internal static readonly Type[] ArrayConstructorParameterTypes = new Type[] { typeof(int) }; | ||
1253 | |||
1254 | private static readonly string[] Iso8601Format = new string[] | ||
1255 | { | ||
1256 | @"yyyy-MM-dd\THH:mm:ss.FFFFFFF\Z", | ||
1257 | @"yyyy-MM-dd\THH:mm:ss\Z", | ||
1258 | @"yyyy-MM-dd\THH:mm:ssK" | ||
1259 | }; | ||
1260 | |||
1261 | public PocoJsonSerializerStrategy() | ||
1262 | { | ||
1263 | ConstructorCache = new ReflectionUtils.ThreadSafeDictionary<Type, ReflectionUtils.ConstructorDelegate>(ContructorDelegateFactory); | ||
1264 | GetCache = new ReflectionUtils.ThreadSafeDictionary<Type, IDictionary<string, ReflectionUtils.GetDelegate>>(GetterValueFactory); | ||
1265 | SetCache = new ReflectionUtils.ThreadSafeDictionary<Type, IDictionary<string, KeyValuePair<Type, ReflectionUtils.SetDelegate>>>(SetterValueFactory); | ||
1266 | } | ||
1267 | |||
1268 | protected virtual string MapClrMemberNameToJsonFieldName(string clrPropertyName) | ||
1269 | { | ||
1270 | return clrPropertyName; | ||
1271 | } | ||
1272 | |||
1273 | internal virtual ReflectionUtils.ConstructorDelegate ContructorDelegateFactory(Type key) | ||
1274 | { | ||
1275 | return ReflectionUtils.GetContructor(key, key.IsArray ? ArrayConstructorParameterTypes : EmptyTypes); | ||
1276 | } | ||
1277 | |||
1278 | internal virtual IDictionary<string, ReflectionUtils.GetDelegate> GetterValueFactory(Type type) | ||
1279 | { | ||
1280 | IDictionary<string, ReflectionUtils.GetDelegate> result = new Dictionary<string, ReflectionUtils.GetDelegate>(); | ||
1281 | foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type)) | ||
1282 | { | ||
1283 | if (propertyInfo.CanRead) | ||
1284 | { | ||
1285 | MethodInfo getMethod = ReflectionUtils.GetGetterMethodInfo(propertyInfo); | ||
1286 | if (getMethod.IsStatic || !getMethod.IsPublic) | ||
1287 | continue; | ||
1288 | result[MapClrMemberNameToJsonFieldName(propertyInfo.Name)] = ReflectionUtils.GetGetMethod(propertyInfo); | ||
1289 | } | ||
1290 | } | ||
1291 | foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) | ||
1292 | { | ||
1293 | if (fieldInfo.IsStatic || !fieldInfo.IsPublic) | ||
1294 | continue; | ||
1295 | result[MapClrMemberNameToJsonFieldName(fieldInfo.Name)] = ReflectionUtils.GetGetMethod(fieldInfo); | ||
1296 | } | ||
1297 | return result; | ||
1298 | } | ||
1299 | |||
1300 | internal virtual IDictionary<string, KeyValuePair<Type, ReflectionUtils.SetDelegate>> SetterValueFactory(Type type) | ||
1301 | { | ||
1302 | IDictionary<string, KeyValuePair<Type, ReflectionUtils.SetDelegate>> result = new Dictionary<string, KeyValuePair<Type, ReflectionUtils.SetDelegate>>(); | ||
1303 | foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type)) | ||
1304 | { | ||
1305 | if (propertyInfo.CanWrite) | ||
1306 | { | ||
1307 | MethodInfo setMethod = ReflectionUtils.GetSetterMethodInfo(propertyInfo); | ||
1308 | if (setMethod.IsStatic || !setMethod.IsPublic) | ||
1309 | continue; | ||
1310 | result[MapClrMemberNameToJsonFieldName(propertyInfo.Name)] = new KeyValuePair<Type, ReflectionUtils.SetDelegate>(propertyInfo.PropertyType, ReflectionUtils.GetSetMethod(propertyInfo)); | ||
1311 | } | ||
1312 | } | ||
1313 | foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) | ||
1314 | { | ||
1315 | if (fieldInfo.IsInitOnly || fieldInfo.IsStatic || !fieldInfo.IsPublic) | ||
1316 | continue; | ||
1317 | result[MapClrMemberNameToJsonFieldName(fieldInfo.Name)] = new KeyValuePair<Type, ReflectionUtils.SetDelegate>(fieldInfo.FieldType, ReflectionUtils.GetSetMethod(fieldInfo)); | ||
1318 | } | ||
1319 | return result; | ||
1320 | } | ||
1321 | |||
1322 | public virtual bool TrySerializeNonPrimitiveObject(object input, out object output) | ||
1323 | { | ||
1324 | return TrySerializeKnownTypes(input, out output) || TrySerializeUnknownTypes(input, out output); | ||
1325 | } | ||
1326 | |||
1327 | [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] | ||
1328 | public virtual object DeserializeObject(object value, Type type) | ||
1329 | { | ||
1330 | if (type == null) throw new ArgumentNullException("type"); | ||
1331 | string str = value as string; | ||
1332 | |||
1333 | if (type == typeof(Guid) && string.IsNullOrEmpty(str)) | ||
1334 | return default(Guid); | ||
1335 | |||
1336 | if (value == null) | ||
1337 | return null; | ||
1338 | |||
1339 | object obj = null; | ||
1340 | |||
1341 | if (str != null) | ||
1342 | { | ||
1343 | if (str.Length != 0) // We know it can't be null now. | ||
1344 | { | ||
1345 | if (type == typeof(DateTime) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(DateTime))) | ||
1346 | return DateTime.ParseExact(str, Iso8601Format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal); | ||
1347 | if (type == typeof(DateTimeOffset) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(DateTimeOffset))) | ||
1348 | return DateTimeOffset.ParseExact(str, Iso8601Format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal); | ||
1349 | if (type == typeof(Guid) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(Guid))) | ||
1350 | return new Guid(str); | ||
1351 | if (type == typeof(Uri)) | ||
1352 | { | ||
1353 | bool isValid = Uri.IsWellFormedUriString(str, UriKind.RelativeOrAbsolute); | ||
1354 | |||
1355 | Uri result; | ||
1356 | if (isValid && Uri.TryCreate(str, UriKind.RelativeOrAbsolute, out result)) | ||
1357 | return result; | ||
1358 | |||
1359 | return null; | ||
1360 | } | ||
1361 | |||
1362 | if (type == typeof(string)) | ||
1363 | return str; | ||
1364 | |||
1365 | return Convert.ChangeType(str, type, CultureInfo.InvariantCulture); | ||
1366 | } | ||
1367 | else | ||
1368 | { | ||
1369 | if (type == typeof(Guid)) | ||
1370 | obj = default(Guid); | ||
1371 | else if (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(Guid)) | ||
1372 | obj = null; | ||
1373 | else | ||
1374 | obj = str; | ||
1375 | } | ||
1376 | // Empty string case | ||
1377 | if (!ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(Guid)) | ||
1378 | return str; | ||
1379 | } | ||
1380 | else if (value is bool) | ||
1381 | return value; | ||
1382 | |||
1383 | bool valueIsLong = value is long; | ||
1384 | bool valueIsDouble = value is double; | ||
1385 | if ((valueIsLong && type == typeof(long)) || (valueIsDouble && type == typeof(double))) | ||
1386 | return value; | ||
1387 | if ((valueIsDouble && type != typeof(double)) || (valueIsLong && type != typeof(long))) | ||
1388 | { | ||
1389 | obj = type == typeof(int) || type == typeof(long) || type == typeof(double) || type == typeof(float) || type == typeof(bool) || type == typeof(decimal) || type == typeof(byte) || type == typeof(short) | ||
1390 | ? Convert.ChangeType(value, type, CultureInfo.InvariantCulture) | ||
1391 | : value; | ||
1392 | } | ||
1393 | else | ||
1394 | { | ||
1395 | IDictionary<string, object> objects = value as IDictionary<string, object>; | ||
1396 | if (objects != null) | ||
1397 | { | ||
1398 | IDictionary<string, object> jsonObject = objects; | ||
1399 | |||
1400 | if (ReflectionUtils.IsTypeDictionary(type)) | ||
1401 | { | ||
1402 | // if dictionary then | ||
1403 | Type[] types = ReflectionUtils.GetGenericTypeArguments(type); | ||
1404 | Type keyType = types[0]; | ||
1405 | Type valueType = types[1]; | ||
1406 | |||
1407 | Type genericType = typeof(Dictionary<,>).MakeGenericType(keyType, valueType); | ||
1408 | |||
1409 | IDictionary dict = (IDictionary)ConstructorCache[genericType](); | ||
1410 | |||
1411 | foreach (KeyValuePair<string, object> kvp in jsonObject) | ||
1412 | dict.Add(kvp.Key, DeserializeObject(kvp.Value, valueType)); | ||
1413 | |||
1414 | obj = dict; | ||
1415 | } | ||
1416 | else | ||
1417 | { | ||
1418 | if (type == typeof(object)) | ||
1419 | obj = value; | ||
1420 | else | ||
1421 | { | ||
1422 | obj = ConstructorCache[type](); | ||
1423 | foreach (KeyValuePair<string, KeyValuePair<Type, ReflectionUtils.SetDelegate>> setter in SetCache[type]) | ||
1424 | { | ||
1425 | object jsonValue; | ||
1426 | if (jsonObject.TryGetValue(setter.Key, out jsonValue)) | ||
1427 | { | ||
1428 | jsonValue = DeserializeObject(jsonValue, setter.Value.Key); | ||
1429 | setter.Value.Value(obj, jsonValue); | ||
1430 | } | ||
1431 | } | ||
1432 | } | ||
1433 | } | ||
1434 | } | ||
1435 | else | ||
1436 | { | ||
1437 | IList<object> valueAsList = value as IList<object>; | ||
1438 | if (valueAsList != null) | ||
1439 | { | ||
1440 | IList<object> jsonObject = valueAsList; | ||
1441 | IList list = null; | ||
1442 | |||
1443 | if (type.IsArray) | ||
1444 | { | ||
1445 | list = (IList)ConstructorCache[type](jsonObject.Count); | ||
1446 | int i = 0; | ||
1447 | foreach (object o in jsonObject) | ||
1448 | list[i++] = DeserializeObject(o, type.GetElementType()); | ||
1449 | } | ||
1450 | else if (ReflectionUtils.IsTypeGenericeCollectionInterface(type) || ReflectionUtils.IsAssignableFrom(typeof(IList), type)) | ||
1451 | { | ||
1452 | Type innerType = ReflectionUtils.GetGenericListElementType(type); | ||
1453 | list = (IList)(ConstructorCache[type] ?? ConstructorCache[typeof(List<>).MakeGenericType(innerType)])(jsonObject.Count); | ||
1454 | foreach (object o in jsonObject) | ||
1455 | list.Add(DeserializeObject(o, innerType)); | ||
1456 | } | ||
1457 | obj = list; | ||
1458 | } | ||
1459 | } | ||
1460 | return obj; | ||
1461 | } | ||
1462 | if (ReflectionUtils.IsNullableType(type)) | ||
1463 | return ReflectionUtils.ToNullableType(obj, type); | ||
1464 | return obj; | ||
1465 | } | ||
1466 | |||
1467 | protected virtual object SerializeEnum(Enum p) | ||
1468 | { | ||
1469 | return Convert.ToDouble(p, CultureInfo.InvariantCulture); | ||
1470 | } | ||
1471 | |||
1472 | [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification = "Need to support .NET 2")] | ||
1473 | protected virtual bool TrySerializeKnownTypes(object input, out object output) | ||
1474 | { | ||
1475 | bool returnValue = true; | ||
1476 | if (input is DateTime) | ||
1477 | output = ((DateTime)input).ToUniversalTime().ToString(Iso8601Format[0], CultureInfo.InvariantCulture); | ||
1478 | else if (input is DateTimeOffset) | ||
1479 | output = ((DateTimeOffset)input).ToUniversalTime().ToString(Iso8601Format[0], CultureInfo.InvariantCulture); | ||
1480 | else if (input is Guid) | ||
1481 | output = ((Guid)input).ToString("D"); | ||
1482 | else if (input is Uri) | ||
1483 | output = input.ToString(); | ||
1484 | else | ||
1485 | { | ||
1486 | Enum inputEnum = input as Enum; | ||
1487 | if (inputEnum != null) | ||
1488 | output = SerializeEnum(inputEnum); | ||
1489 | else | ||
1490 | { | ||
1491 | returnValue = false; | ||
1492 | output = null; | ||
1493 | } | ||
1494 | } | ||
1495 | return returnValue; | ||
1496 | } | ||
1497 | [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate", Justification = "Need to support .NET 2")] | ||
1498 | protected virtual bool TrySerializeUnknownTypes(object input, out object output) | ||
1499 | { | ||
1500 | if (input == null) throw new ArgumentNullException("input"); | ||
1501 | output = null; | ||
1502 | Type type = input.GetType(); | ||
1503 | if (type.FullName == null) | ||
1504 | return false; | ||
1505 | IDictionary<string, object> obj = new JsonObject(); | ||
1506 | IDictionary<string, ReflectionUtils.GetDelegate> getters = GetCache[type]; | ||
1507 | foreach (KeyValuePair<string, ReflectionUtils.GetDelegate> getter in getters) | ||
1508 | { | ||
1509 | if (getter.Value != null) | ||
1510 | obj.Add(MapClrMemberNameToJsonFieldName(getter.Key), getter.Value(input)); | ||
1511 | } | ||
1512 | output = obj; | ||
1513 | return true; | ||
1514 | } | ||
1515 | } | ||
1516 | |||
1517 | #if SIMPLE_JSON_DATACONTRACT | ||
1518 | [GeneratedCode("simple-json", "1.0.0")] | ||
1519 | #if SIMPLE_JSON_INTERNAL | ||
1520 | internal | ||
1521 | #else | ||
1522 | public | ||
1523 | #endif | ||
1524 | class DataContractJsonSerializerStrategy : PocoJsonSerializerStrategy | ||
1525 | { | ||
1526 | public DataContractJsonSerializerStrategy() | ||
1527 | { | ||
1528 | GetCache = new ReflectionUtils.ThreadSafeDictionary<Type, IDictionary<string, ReflectionUtils.GetDelegate>>(GetterValueFactory); | ||
1529 | SetCache = new ReflectionUtils.ThreadSafeDictionary<Type, IDictionary<string, KeyValuePair<Type, ReflectionUtils.SetDelegate>>>(SetterValueFactory); | ||
1530 | } | ||
1531 | |||
1532 | internal override IDictionary<string, ReflectionUtils.GetDelegate> GetterValueFactory(Type type) | ||
1533 | { | ||
1534 | bool hasDataContract = ReflectionUtils.GetAttribute(type, typeof(DataContractAttribute)) != null; | ||
1535 | if (!hasDataContract) | ||
1536 | return base.GetterValueFactory(type); | ||
1537 | string jsonKey; | ||
1538 | IDictionary<string, ReflectionUtils.GetDelegate> result = new Dictionary<string, ReflectionUtils.GetDelegate>(); | ||
1539 | foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type)) | ||
1540 | { | ||
1541 | if (propertyInfo.CanRead) | ||
1542 | { | ||
1543 | MethodInfo getMethod = ReflectionUtils.GetGetterMethodInfo(propertyInfo); | ||
1544 | if (!getMethod.IsStatic && CanAdd(propertyInfo, out jsonKey)) | ||
1545 | result[jsonKey] = ReflectionUtils.GetGetMethod(propertyInfo); | ||
1546 | } | ||
1547 | } | ||
1548 | foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) | ||
1549 | { | ||
1550 | if (!fieldInfo.IsStatic && CanAdd(fieldInfo, out jsonKey)) | ||
1551 | result[jsonKey] = ReflectionUtils.GetGetMethod(fieldInfo); | ||
1552 | } | ||
1553 | return result; | ||
1554 | } | ||
1555 | |||
1556 | internal override IDictionary<string, KeyValuePair<Type, ReflectionUtils.SetDelegate>> SetterValueFactory(Type type) | ||
1557 | { | ||
1558 | bool hasDataContract = ReflectionUtils.GetAttribute(type, typeof(DataContractAttribute)) != null; | ||
1559 | if (!hasDataContract) | ||
1560 | return base.SetterValueFactory(type); | ||
1561 | string jsonKey; | ||
1562 | IDictionary<string, KeyValuePair<Type, ReflectionUtils.SetDelegate>> result = new Dictionary<string, KeyValuePair<Type, ReflectionUtils.SetDelegate>>(); | ||
1563 | foreach (PropertyInfo propertyInfo in ReflectionUtils.GetProperties(type)) | ||
1564 | { | ||
1565 | if (propertyInfo.CanWrite) | ||
1566 | { | ||
1567 | MethodInfo setMethod = ReflectionUtils.GetSetterMethodInfo(propertyInfo); | ||
1568 | if (!setMethod.IsStatic && CanAdd(propertyInfo, out jsonKey)) | ||
1569 | result[jsonKey] = new KeyValuePair<Type, ReflectionUtils.SetDelegate>(propertyInfo.PropertyType, ReflectionUtils.GetSetMethod(propertyInfo)); | ||
1570 | } | ||
1571 | } | ||
1572 | foreach (FieldInfo fieldInfo in ReflectionUtils.GetFields(type)) | ||
1573 | { | ||
1574 | if (!fieldInfo.IsInitOnly && !fieldInfo.IsStatic && CanAdd(fieldInfo, out jsonKey)) | ||
1575 | result[jsonKey] = new KeyValuePair<Type, ReflectionUtils.SetDelegate>(fieldInfo.FieldType, ReflectionUtils.GetSetMethod(fieldInfo)); | ||
1576 | } | ||
1577 | // todo implement sorting for DATACONTRACT. | ||
1578 | return result; | ||
1579 | } | ||
1580 | |||
1581 | private static bool CanAdd(MemberInfo info, out string jsonKey) | ||
1582 | { | ||
1583 | jsonKey = null; | ||
1584 | if (ReflectionUtils.GetAttribute(info, typeof(IgnoreDataMemberAttribute)) != null) | ||
1585 | return false; | ||
1586 | DataMemberAttribute dataMemberAttribute = (DataMemberAttribute)ReflectionUtils.GetAttribute(info, typeof(DataMemberAttribute)); | ||
1587 | if (dataMemberAttribute == null) | ||
1588 | return false; | ||
1589 | jsonKey = string.IsNullOrEmpty(dataMemberAttribute.Name) ? info.Name : dataMemberAttribute.Name; | ||
1590 | return true; | ||
1591 | } | ||
1592 | } | ||
1593 | |||
1594 | #endif | ||
1595 | |||
1596 | namespace Reflection | ||
1597 | { | ||
1598 | // This class is meant to be copied into other libraries. So we want to exclude it from Code Analysis rules | ||
1599 | // that might be in place in the target project. | ||
1600 | [GeneratedCode("reflection-utils", "1.0.0")] | ||
1601 | #if SIMPLE_JSON_REFLECTION_UTILS_PUBLIC | ||
1602 | public | ||
1603 | #else | ||
1604 | internal | ||
1605 | #endif | ||
1606 | class ReflectionUtils | ||
1607 | { | ||
1608 | private static readonly object[] EmptyObjects = new object[] { }; | ||
1609 | |||
1610 | public delegate object GetDelegate(object source); | ||
1611 | public delegate void SetDelegate(object source, object value); | ||
1612 | public delegate object ConstructorDelegate(params object[] args); | ||
1613 | |||
1614 | public delegate TValue ThreadSafeDictionaryValueFactory<TKey, TValue>(TKey key); | ||
1615 | |||
1616 | #if SIMPLE_JSON_TYPEINFO | ||
1617 | public static TypeInfo GetTypeInfo(Type type) | ||
1618 | { | ||
1619 | return type.GetTypeInfo(); | ||
1620 | } | ||
1621 | #else | ||
1622 | public static Type GetTypeInfo(Type type) | ||
1623 | { | ||
1624 | return type; | ||
1625 | } | ||
1626 | #endif | ||
1627 | |||
1628 | public static Attribute GetAttribute(MemberInfo info, Type type) | ||
1629 | { | ||
1630 | #if SIMPLE_JSON_TYPEINFO | ||
1631 | if (info == null || type == null || !info.IsDefined(type)) | ||
1632 | return null; | ||
1633 | return info.GetCustomAttribute(type); | ||
1634 | #else | ||
1635 | if (info == null || type == null || !Attribute.IsDefined(info, type)) | ||
1636 | return null; | ||
1637 | return Attribute.GetCustomAttribute(info, type); | ||
1638 | #endif | ||
1639 | } | ||
1640 | |||
1641 | public static Type GetGenericListElementType(Type type) | ||
1642 | { | ||
1643 | IEnumerable<Type> interfaces; | ||
1644 | #if SIMPLE_JSON_TYPEINFO | ||
1645 | interfaces = type.GetTypeInfo().ImplementedInterfaces; | ||
1646 | #else | ||
1647 | interfaces = type.GetInterfaces(); | ||
1648 | #endif | ||
1649 | foreach (Type implementedInterface in interfaces) | ||
1650 | { | ||
1651 | if (IsTypeGeneric(implementedInterface) && | ||
1652 | implementedInterface.GetGenericTypeDefinition() == typeof(IList<>)) | ||
1653 | { | ||
1654 | return GetGenericTypeArguments(implementedInterface)[0]; | ||
1655 | } | ||
1656 | } | ||
1657 | return GetGenericTypeArguments(type)[0]; | ||
1658 | } | ||
1659 | |||
1660 | public static Attribute GetAttribute(Type objectType, Type attributeType) | ||
1661 | { | ||
1662 | |||
1663 | #if SIMPLE_JSON_TYPEINFO | ||
1664 | if (objectType == null || attributeType == null || !objectType.GetTypeInfo().IsDefined(attributeType)) | ||
1665 | return null; | ||
1666 | return objectType.GetTypeInfo().GetCustomAttribute(attributeType); | ||
1667 | #else | ||
1668 | if (objectType == null || attributeType == null || !Attribute.IsDefined(objectType, attributeType)) | ||
1669 | return null; | ||
1670 | return Attribute.GetCustomAttribute(objectType, attributeType); | ||
1671 | #endif | ||
1672 | } | ||
1673 | |||
1674 | public static Type[] GetGenericTypeArguments(Type type) | ||
1675 | { | ||
1676 | #if SIMPLE_JSON_TYPEINFO | ||
1677 | return type.GetTypeInfo().GenericTypeArguments; | ||
1678 | #else | ||
1679 | return type.GetGenericArguments(); | ||
1680 | #endif | ||
1681 | } | ||
1682 | |||
1683 | public static bool IsTypeGeneric(Type type) | ||
1684 | { | ||
1685 | return GetTypeInfo(type).IsGenericType; | ||
1686 | } | ||
1687 | |||
1688 | public static bool IsTypeGenericeCollectionInterface(Type type) | ||
1689 | { | ||
1690 | if (!IsTypeGeneric(type)) | ||
1691 | return false; | ||
1692 | |||
1693 | Type genericDefinition = type.GetGenericTypeDefinition(); | ||
1694 | |||
1695 | return (genericDefinition == typeof(IList<>) | ||
1696 | || genericDefinition == typeof(ICollection<>) | ||
1697 | || genericDefinition == typeof(IEnumerable<>) | ||
1698 | #if SIMPLE_JSON_READONLY_COLLECTIONS | ||
1699 | || genericDefinition == typeof(IReadOnlyCollection<>) | ||
1700 | || genericDefinition == typeof(IReadOnlyList<>) | ||
1701 | #endif | ||
1702 | ); | ||
1703 | } | ||
1704 | |||
1705 | public static bool IsAssignableFrom(Type type1, Type type2) | ||
1706 | { | ||
1707 | return GetTypeInfo(type1).IsAssignableFrom(GetTypeInfo(type2)); | ||
1708 | } | ||
1709 | |||
1710 | public static bool IsTypeDictionary(Type type) | ||
1711 | { | ||
1712 | #if SIMPLE_JSON_TYPEINFO | ||
1713 | if (typeof(IDictionary<,>).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) | ||
1714 | return true; | ||
1715 | #else | ||
1716 | if (typeof(System.Collections.IDictionary).IsAssignableFrom(type)) | ||
1717 | return true; | ||
1718 | #endif | ||
1719 | if (!GetTypeInfo(type).IsGenericType) | ||
1720 | return false; | ||
1721 | |||
1722 | Type genericDefinition = type.GetGenericTypeDefinition(); | ||
1723 | return genericDefinition == typeof(IDictionary<,>); | ||
1724 | } | ||
1725 | |||
1726 | public static bool IsNullableType(Type type) | ||
1727 | { | ||
1728 | return GetTypeInfo(type).IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); | ||
1729 | } | ||
1730 | |||
1731 | public static object ToNullableType(object obj, Type nullableType) | ||
1732 | { | ||
1733 | return obj == null ? null : Convert.ChangeType(obj, Nullable.GetUnderlyingType(nullableType), CultureInfo.InvariantCulture); | ||
1734 | } | ||
1735 | |||
1736 | public static bool IsValueType(Type type) | ||
1737 | { | ||
1738 | return GetTypeInfo(type).IsValueType; | ||
1739 | } | ||
1740 | |||
1741 | public static IEnumerable<ConstructorInfo> GetConstructors(Type type) | ||
1742 | { | ||
1743 | #if SIMPLE_JSON_TYPEINFO | ||
1744 | return type.GetTypeInfo().DeclaredConstructors; | ||
1745 | #else | ||
1746 | return type.GetConstructors(); | ||
1747 | #endif | ||
1748 | } | ||
1749 | |||
1750 | public static ConstructorInfo GetConstructorInfo(Type type, params Type[] argsType) | ||
1751 | { | ||
1752 | IEnumerable<ConstructorInfo> constructorInfos = GetConstructors(type); | ||
1753 | int i; | ||
1754 | bool matches; | ||
1755 | foreach (ConstructorInfo constructorInfo in constructorInfos) | ||
1756 | { | ||
1757 | ParameterInfo[] parameters = constructorInfo.GetParameters(); | ||
1758 | if (argsType.Length != parameters.Length) | ||
1759 | continue; | ||
1760 | |||
1761 | i = 0; | ||
1762 | matches = true; | ||
1763 | foreach (ParameterInfo parameterInfo in constructorInfo.GetParameters()) | ||
1764 | { | ||
1765 | if (parameterInfo.ParameterType != argsType[i]) | ||
1766 | { | ||
1767 | matches = false; | ||
1768 | break; | ||
1769 | } | ||
1770 | } | ||
1771 | |||
1772 | if (matches) | ||
1773 | return constructorInfo; | ||
1774 | } | ||
1775 | |||
1776 | return null; | ||
1777 | } | ||
1778 | |||
1779 | public static IEnumerable<PropertyInfo> GetProperties(Type type) | ||
1780 | { | ||
1781 | #if SIMPLE_JSON_TYPEINFO | ||
1782 | return type.GetRuntimeProperties(); | ||
1783 | #else | ||
1784 | return type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); | ||
1785 | #endif | ||
1786 | } | ||
1787 | |||
1788 | public static IEnumerable<FieldInfo> GetFields(Type type) | ||
1789 | { | ||
1790 | #if SIMPLE_JSON_TYPEINFO | ||
1791 | return type.GetRuntimeFields(); | ||
1792 | #else | ||
1793 | return type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); | ||
1794 | #endif | ||
1795 | } | ||
1796 | |||
1797 | public static MethodInfo GetGetterMethodInfo(PropertyInfo propertyInfo) | ||
1798 | { | ||
1799 | #if SIMPLE_JSON_TYPEINFO | ||
1800 | return propertyInfo.GetMethod; | ||
1801 | #else | ||
1802 | return propertyInfo.GetGetMethod(true); | ||
1803 | #endif | ||
1804 | } | ||
1805 | |||
1806 | public static MethodInfo GetSetterMethodInfo(PropertyInfo propertyInfo) | ||
1807 | { | ||
1808 | #if SIMPLE_JSON_TYPEINFO | ||
1809 | return propertyInfo.SetMethod; | ||
1810 | #else | ||
1811 | return propertyInfo.GetSetMethod(true); | ||
1812 | #endif | ||
1813 | } | ||
1814 | |||
1815 | public static ConstructorDelegate GetContructor(ConstructorInfo constructorInfo) | ||
1816 | { | ||
1817 | #if SIMPLE_JSON_NO_LINQ_EXPRESSION | ||
1818 | return GetConstructorByReflection(constructorInfo); | ||
1819 | #else | ||
1820 | return GetConstructorByExpression(constructorInfo); | ||
1821 | #endif | ||
1822 | } | ||
1823 | |||
1824 | public static ConstructorDelegate GetContructor(Type type, params Type[] argsType) | ||
1825 | { | ||
1826 | #if SIMPLE_JSON_NO_LINQ_EXPRESSION | ||
1827 | return GetConstructorByReflection(type, argsType); | ||
1828 | #else | ||
1829 | return GetConstructorByExpression(type, argsType); | ||
1830 | #endif | ||
1831 | } | ||
1832 | |||
1833 | public static ConstructorDelegate GetConstructorByReflection(ConstructorInfo constructorInfo) | ||
1834 | { | ||
1835 | return delegate (object[] args) { return constructorInfo.Invoke(args); }; | ||
1836 | } | ||
1837 | |||
1838 | public static ConstructorDelegate GetConstructorByReflection(Type type, params Type[] argsType) | ||
1839 | { | ||
1840 | ConstructorInfo constructorInfo = GetConstructorInfo(type, argsType); | ||
1841 | return constructorInfo == null ? null : GetConstructorByReflection(constructorInfo); | ||
1842 | } | ||
1843 | |||
1844 | #if !SIMPLE_JSON_NO_LINQ_EXPRESSION | ||
1845 | |||
1846 | public static ConstructorDelegate GetConstructorByExpression(ConstructorInfo constructorInfo) | ||
1847 | { | ||
1848 | ParameterInfo[] paramsInfo = constructorInfo.GetParameters(); | ||
1849 | ParameterExpression param = Expression.Parameter(typeof(object[]), "args"); | ||
1850 | Expression[] argsExp = new Expression[paramsInfo.Length]; | ||
1851 | for (int i = 0; i < paramsInfo.Length; i++) | ||
1852 | { | ||
1853 | Expression index = Expression.Constant(i); | ||
1854 | Type paramType = paramsInfo[i].ParameterType; | ||
1855 | Expression paramAccessorExp = Expression.ArrayIndex(param, index); | ||
1856 | Expression paramCastExp = Expression.Convert(paramAccessorExp, paramType); | ||
1857 | argsExp[i] = paramCastExp; | ||
1858 | } | ||
1859 | NewExpression newExp = Expression.New(constructorInfo, argsExp); | ||
1860 | Expression<Func<object[], object>> lambda = Expression.Lambda<Func<object[], object>>(newExp, param); | ||
1861 | Func<object[], object> compiledLambda = lambda.Compile(); | ||
1862 | return delegate (object[] args) { return compiledLambda(args); }; | ||
1863 | } | ||
1864 | |||
1865 | public static ConstructorDelegate GetConstructorByExpression(Type type, params Type[] argsType) | ||
1866 | { | ||
1867 | ConstructorInfo constructorInfo = GetConstructorInfo(type, argsType); | ||
1868 | return constructorInfo == null ? null : GetConstructorByExpression(constructorInfo); | ||
1869 | } | ||
1870 | |||
1871 | #endif | ||
1872 | |||
1873 | public static GetDelegate GetGetMethod(PropertyInfo propertyInfo) | ||
1874 | { | ||
1875 | #if SIMPLE_JSON_NO_LINQ_EXPRESSION | ||
1876 | return GetGetMethodByReflection(propertyInfo); | ||
1877 | #else | ||
1878 | return GetGetMethodByExpression(propertyInfo); | ||
1879 | #endif | ||
1880 | } | ||
1881 | |||
1882 | public static GetDelegate GetGetMethod(FieldInfo fieldInfo) | ||
1883 | { | ||
1884 | #if SIMPLE_JSON_NO_LINQ_EXPRESSION | ||
1885 | return GetGetMethodByReflection(fieldInfo); | ||
1886 | #else | ||
1887 | return GetGetMethodByExpression(fieldInfo); | ||
1888 | #endif | ||
1889 | } | ||
1890 | |||
1891 | public static GetDelegate GetGetMethodByReflection(PropertyInfo propertyInfo) | ||
1892 | { | ||
1893 | MethodInfo methodInfo = GetGetterMethodInfo(propertyInfo); | ||
1894 | return delegate (object source) { return methodInfo.Invoke(source, EmptyObjects); }; | ||
1895 | } | ||
1896 | |||
1897 | public static GetDelegate GetGetMethodByReflection(FieldInfo fieldInfo) | ||
1898 | { | ||
1899 | return delegate (object source) { return fieldInfo.GetValue(source); }; | ||
1900 | } | ||
1901 | |||
1902 | #if !SIMPLE_JSON_NO_LINQ_EXPRESSION | ||
1903 | |||
1904 | public static GetDelegate GetGetMethodByExpression(PropertyInfo propertyInfo) | ||
1905 | { | ||
1906 | MethodInfo getMethodInfo = GetGetterMethodInfo(propertyInfo); | ||
1907 | ParameterExpression instance = Expression.Parameter(typeof(object), "instance"); | ||
1908 | UnaryExpression instanceCast = (!IsValueType(propertyInfo.DeclaringType)) ? Expression.TypeAs(instance, propertyInfo.DeclaringType) : Expression.Convert(instance, propertyInfo.DeclaringType); | ||
1909 | Func<object, object> compiled = Expression.Lambda<Func<object, object>>(Expression.TypeAs(Expression.Call(instanceCast, getMethodInfo), typeof(object)), instance).Compile(); | ||
1910 | return delegate (object source) { return compiled(source); }; | ||
1911 | } | ||
1912 | |||
1913 | public static GetDelegate GetGetMethodByExpression(FieldInfo fieldInfo) | ||
1914 | { | ||
1915 | ParameterExpression instance = Expression.Parameter(typeof(object), "instance"); | ||
1916 | MemberExpression member = Expression.Field(Expression.Convert(instance, fieldInfo.DeclaringType), fieldInfo); | ||
1917 | GetDelegate compiled = Expression.Lambda<GetDelegate>(Expression.Convert(member, typeof(object)), instance).Compile(); | ||
1918 | return delegate (object source) { return compiled(source); }; | ||
1919 | } | ||
1920 | |||
1921 | #endif | ||
1922 | |||
1923 | public static SetDelegate GetSetMethod(PropertyInfo propertyInfo) | ||
1924 | { | ||
1925 | #if SIMPLE_JSON_NO_LINQ_EXPRESSION | ||
1926 | return GetSetMethodByReflection(propertyInfo); | ||
1927 | #else | ||
1928 | return GetSetMethodByExpression(propertyInfo); | ||
1929 | #endif | ||
1930 | } | ||
1931 | |||
1932 | public static SetDelegate GetSetMethod(FieldInfo fieldInfo) | ||
1933 | { | ||
1934 | #if SIMPLE_JSON_NO_LINQ_EXPRESSION | ||
1935 | return GetSetMethodByReflection(fieldInfo); | ||
1936 | #else | ||
1937 | return GetSetMethodByExpression(fieldInfo); | ||
1938 | #endif | ||
1939 | } | ||
1940 | |||
1941 | public static SetDelegate GetSetMethodByReflection(PropertyInfo propertyInfo) | ||
1942 | { | ||
1943 | MethodInfo methodInfo = GetSetterMethodInfo(propertyInfo); | ||
1944 | return delegate (object source, object value) { methodInfo.Invoke(source, new object[] { value }); }; | ||
1945 | } | ||
1946 | |||
1947 | public static SetDelegate GetSetMethodByReflection(FieldInfo fieldInfo) | ||
1948 | { | ||
1949 | return delegate (object source, object value) { fieldInfo.SetValue(source, value); }; | ||
1950 | } | ||
1951 | |||
1952 | #if !SIMPLE_JSON_NO_LINQ_EXPRESSION | ||
1953 | |||
1954 | public static SetDelegate GetSetMethodByExpression(PropertyInfo propertyInfo) | ||
1955 | { | ||
1956 | MethodInfo setMethodInfo = GetSetterMethodInfo(propertyInfo); | ||
1957 | ParameterExpression instance = Expression.Parameter(typeof(object), "instance"); | ||
1958 | ParameterExpression value = Expression.Parameter(typeof(object), "value"); | ||
1959 | UnaryExpression instanceCast = (!IsValueType(propertyInfo.DeclaringType)) ? Expression.TypeAs(instance, propertyInfo.DeclaringType) : Expression.Convert(instance, propertyInfo.DeclaringType); | ||
1960 | UnaryExpression valueCast = (!IsValueType(propertyInfo.PropertyType)) ? Expression.TypeAs(value, propertyInfo.PropertyType) : Expression.Convert(value, propertyInfo.PropertyType); | ||
1961 | Action<object, object> compiled = Expression.Lambda<Action<object, object>>(Expression.Call(instanceCast, setMethodInfo, valueCast), new ParameterExpression[] { instance, value }).Compile(); | ||
1962 | return delegate (object source, object val) { compiled(source, val); }; | ||
1963 | } | ||
1964 | |||
1965 | public static SetDelegate GetSetMethodByExpression(FieldInfo fieldInfo) | ||
1966 | { | ||
1967 | ParameterExpression instance = Expression.Parameter(typeof(object), "instance"); | ||
1968 | ParameterExpression value = Expression.Parameter(typeof(object), "value"); | ||
1969 | Action<object, object> compiled = Expression.Lambda<Action<object, object>>( | ||
1970 | Assign(Expression.Field(Expression.Convert(instance, fieldInfo.DeclaringType), fieldInfo), Expression.Convert(value, fieldInfo.FieldType)), instance, value).Compile(); | ||
1971 | return delegate (object source, object val) { compiled(source, val); }; | ||
1972 | } | ||
1973 | |||
1974 | public static BinaryExpression Assign(Expression left, Expression right) | ||
1975 | { | ||
1976 | #if SIMPLE_JSON_TYPEINFO | ||
1977 | return Expression.Assign(left, right); | ||
1978 | #else | ||
1979 | MethodInfo assign = typeof(Assigner<>).MakeGenericType(left.Type).GetMethod("Assign"); | ||
1980 | BinaryExpression assignExpr = Expression.Add(left, right, assign); | ||
1981 | return assignExpr; | ||
1982 | #endif | ||
1983 | } | ||
1984 | |||
1985 | private static class Assigner<T> | ||
1986 | { | ||
1987 | public static T Assign(ref T left, T right) | ||
1988 | { | ||
1989 | return (left = right); | ||
1990 | } | ||
1991 | } | ||
1992 | |||
1993 | #endif | ||
1994 | |||
1995 | public sealed class ThreadSafeDictionary<TKey, TValue> : IDictionary<TKey, TValue> | ||
1996 | { | ||
1997 | private readonly object _lock = new object(); | ||
1998 | private readonly ThreadSafeDictionaryValueFactory<TKey, TValue> _valueFactory; | ||
1999 | private Dictionary<TKey, TValue> _dictionary; | ||
2000 | |||
2001 | public ThreadSafeDictionary(ThreadSafeDictionaryValueFactory<TKey, TValue> valueFactory) | ||
2002 | { | ||
2003 | _valueFactory = valueFactory; | ||
2004 | } | ||
2005 | |||
2006 | private TValue Get(TKey key) | ||
2007 | { | ||
2008 | if (_dictionary == null) | ||
2009 | return AddValue(key); | ||
2010 | TValue value; | ||
2011 | if (!_dictionary.TryGetValue(key, out value)) | ||
2012 | return AddValue(key); | ||
2013 | return value; | ||
2014 | } | ||
2015 | |||
2016 | private TValue AddValue(TKey key) | ||
2017 | { | ||
2018 | TValue value = _valueFactory(key); | ||
2019 | lock (_lock) | ||
2020 | { | ||
2021 | if (_dictionary == null) | ||
2022 | { | ||
2023 | _dictionary = new Dictionary<TKey, TValue>(); | ||
2024 | _dictionary[key] = value; | ||
2025 | } | ||
2026 | else | ||
2027 | { | ||
2028 | TValue val; | ||
2029 | if (_dictionary.TryGetValue(key, out val)) | ||
2030 | return val; | ||
2031 | Dictionary<TKey, TValue> dict = new Dictionary<TKey, TValue>(_dictionary); | ||
2032 | dict[key] = value; | ||
2033 | _dictionary = dict; | ||
2034 | } | ||
2035 | } | ||
2036 | return value; | ||
2037 | } | ||
2038 | |||
2039 | public void Add(TKey key, TValue value) | ||
2040 | { | ||
2041 | throw new NotImplementedException(); | ||
2042 | } | ||
2043 | |||
2044 | public bool ContainsKey(TKey key) | ||
2045 | { | ||
2046 | return _dictionary.ContainsKey(key); | ||
2047 | } | ||
2048 | |||
2049 | public ICollection<TKey> Keys | ||
2050 | { | ||
2051 | get { return _dictionary.Keys; } | ||
2052 | } | ||
2053 | |||
2054 | public bool Remove(TKey key) | ||
2055 | { | ||
2056 | throw new NotImplementedException(); | ||
2057 | } | ||
2058 | |||
2059 | public bool TryGetValue(TKey key, out TValue value) | ||
2060 | { | ||
2061 | value = this[key]; | ||
2062 | return true; | ||
2063 | } | ||
2064 | |||
2065 | public ICollection<TValue> Values | ||
2066 | { | ||
2067 | get { return _dictionary.Values; } | ||
2068 | } | ||
2069 | |||
2070 | public TValue this[TKey key] | ||
2071 | { | ||
2072 | get { return Get(key); } | ||
2073 | set { throw new NotImplementedException(); } | ||
2074 | } | ||
2075 | |||
2076 | public void Add(KeyValuePair<TKey, TValue> item) | ||
2077 | { | ||
2078 | throw new NotImplementedException(); | ||
2079 | } | ||
2080 | |||
2081 | public void Clear() | ||
2082 | { | ||
2083 | throw new NotImplementedException(); | ||
2084 | } | ||
2085 | |||
2086 | public bool Contains(KeyValuePair<TKey, TValue> item) | ||
2087 | { | ||
2088 | throw new NotImplementedException(); | ||
2089 | } | ||
2090 | |||
2091 | public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) | ||
2092 | { | ||
2093 | throw new NotImplementedException(); | ||
2094 | } | ||
2095 | |||
2096 | public int Count | ||
2097 | { | ||
2098 | get { return _dictionary.Count; } | ||
2099 | } | ||
2100 | |||
2101 | public bool IsReadOnly | ||
2102 | { | ||
2103 | get { throw new NotImplementedException(); } | ||
2104 | } | ||
2105 | |||
2106 | public bool Remove(KeyValuePair<TKey, TValue> item) | ||
2107 | { | ||
2108 | throw new NotImplementedException(); | ||
2109 | } | ||
2110 | |||
2111 | public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() | ||
2112 | { | ||
2113 | return _dictionary.GetEnumerator(); | ||
2114 | } | ||
2115 | |||
2116 | System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() | ||
2117 | { | ||
2118 | return _dictionary.GetEnumerator(); | ||
2119 | } | ||
2120 | } | ||
2121 | |||
2122 | } | ||
2123 | } | ||
2124 | } | ||
2125 | // ReSharper restore LoopCanBeConvertedToQuery | ||
2126 | // ReSharper restore RedundantExplicitArrayCreation | ||
2127 | // ReSharper restore SuggestUseVarKeywordEvident \ No newline at end of file | ||
diff --git a/src/internal/TablesAndTuples/TablesAndTuples.csproj b/src/internal/TablesAndTuples/TablesAndTuples.csproj new file mode 100644 index 00000000..ce1697ae --- /dev/null +++ b/src/internal/TablesAndTuples/TablesAndTuples.csproj | |||
@@ -0,0 +1,8 @@ | |||
1 | <Project Sdk="Microsoft.NET.Sdk"> | ||
2 | |||
3 | <PropertyGroup> | ||
4 | <OutputType>Exe</OutputType> | ||
5 | <TargetFramework>netcoreapp2.0</TargetFramework> | ||
6 | </PropertyGroup> | ||
7 | |||
8 | </Project> | ||
diff --git a/src/internal/TablesAndTuples/WixColumnDefinition.cs b/src/internal/TablesAndTuples/WixColumnDefinition.cs new file mode 100644 index 00000000..2d60c9dc --- /dev/null +++ b/src/internal/TablesAndTuples/WixColumnDefinition.cs | |||
@@ -0,0 +1,296 @@ | |||
1 | using System; | ||
2 | using System.Xml; | ||
3 | |||
4 | namespace TablesAndSymbols | ||
5 | { | ||
6 | class WixColumnDefinition | ||
7 | { | ||
8 | public WixColumnDefinition(string name, ColumnType type, int length, bool primaryKey, bool nullable, ColumnCategory category, long? minValue = null, long? maxValue = null, string keyTable = null, int? keyColumn = null, string possibilities = null, string description = null, ColumnModularizeType? modularizeType = null, bool forceLocalizable = false, bool useCData = false, bool unreal = false) | ||
9 | { | ||
10 | this.Name = name; | ||
11 | this.Type = type; | ||
12 | this.Length = length; | ||
13 | this.PrimaryKey = primaryKey; | ||
14 | this.Nullable = nullable; | ||
15 | this.ModularizeType = modularizeType; | ||
16 | this.ForceLocalizable = forceLocalizable; | ||
17 | this.MinValue = minValue; | ||
18 | this.MaxValue = maxValue; | ||
19 | this.KeyTable = keyTable; | ||
20 | this.KeyColumn = keyColumn; | ||
21 | this.Category = category; | ||
22 | this.Possibilities = possibilities; | ||
23 | this.Description = description; | ||
24 | this.UseCData = useCData; | ||
25 | this.Unreal = unreal; | ||
26 | } | ||
27 | |||
28 | public string Name { get; } | ||
29 | public ColumnType Type { get; } | ||
30 | public int Length { get; } | ||
31 | public bool PrimaryKey { get; } | ||
32 | public bool Nullable { get; } | ||
33 | public ColumnModularizeType? ModularizeType { get; } | ||
34 | public bool ForceLocalizable { get; } | ||
35 | public long? MinValue { get; } | ||
36 | public long? MaxValue { get; } | ||
37 | public string KeyTable { get; } | ||
38 | public int? KeyColumn { get; } | ||
39 | public ColumnCategory Category { get; } | ||
40 | public string Possibilities { get; } | ||
41 | public string Description { get; } | ||
42 | public bool UseCData { get; } | ||
43 | public bool Unreal { get; } | ||
44 | |||
45 | internal static WixColumnDefinition Read(XmlReader reader) | ||
46 | { | ||
47 | if (!reader.LocalName.Equals("columnDefinition")) | ||
48 | { | ||
49 | throw new XmlException(); | ||
50 | } | ||
51 | |||
52 | ColumnCategory category = ColumnCategory.Unknown; | ||
53 | string description = null; | ||
54 | bool empty = reader.IsEmptyElement; | ||
55 | int? keyColumn = null; | ||
56 | string keyTable = null; | ||
57 | int length = -1; | ||
58 | bool localizable = false; | ||
59 | long? maxValue = null; | ||
60 | long? minValue = null; | ||
61 | var modularize = ColumnModularizeType.None; | ||
62 | string name = null; | ||
63 | bool nullable = false; | ||
64 | string possibilities = null; | ||
65 | bool primaryKey = false; | ||
66 | var type = ColumnType.Unknown; | ||
67 | bool useCData = false; | ||
68 | bool unreal = false; | ||
69 | |||
70 | // parse the attributes | ||
71 | while (reader.MoveToNextAttribute()) | ||
72 | { | ||
73 | switch (reader.LocalName) | ||
74 | { | ||
75 | case "category": | ||
76 | switch (reader.Value) | ||
77 | { | ||
78 | case "anyPath": | ||
79 | category = ColumnCategory.AnyPath; | ||
80 | break; | ||
81 | case "binary": | ||
82 | category = ColumnCategory.Binary; | ||
83 | break; | ||
84 | case "cabinet": | ||
85 | category = ColumnCategory.Cabinet; | ||
86 | break; | ||
87 | case "condition": | ||
88 | category = ColumnCategory.Condition; | ||
89 | break; | ||
90 | case "customSource": | ||
91 | category = ColumnCategory.CustomSource; | ||
92 | break; | ||
93 | case "defaultDir": | ||
94 | category = ColumnCategory.DefaultDir; | ||
95 | break; | ||
96 | case "doubleInteger": | ||
97 | category = ColumnCategory.DoubleInteger; | ||
98 | break; | ||
99 | case "filename": | ||
100 | category = ColumnCategory.Filename; | ||
101 | break; | ||
102 | case "formatted": | ||
103 | category = ColumnCategory.Formatted; | ||
104 | break; | ||
105 | case "formattedSddl": | ||
106 | category = ColumnCategory.FormattedSDDLText; | ||
107 | break; | ||
108 | case "guid": | ||
109 | category = ColumnCategory.Guid; | ||
110 | break; | ||
111 | case "identifier": | ||
112 | category = ColumnCategory.Identifier; | ||
113 | break; | ||
114 | case "integer": | ||
115 | category = ColumnCategory.Integer; | ||
116 | break; | ||
117 | case "language": | ||
118 | category = ColumnCategory.Language; | ||
119 | break; | ||
120 | case "lowerCase": | ||
121 | category = ColumnCategory.LowerCase; | ||
122 | break; | ||
123 | case "path": | ||
124 | category = ColumnCategory.Path; | ||
125 | break; | ||
126 | case "paths": | ||
127 | category = ColumnCategory.Paths; | ||
128 | break; | ||
129 | case "property": | ||
130 | category = ColumnCategory.Property; | ||
131 | break; | ||
132 | case "regPath": | ||
133 | category = ColumnCategory.RegPath; | ||
134 | break; | ||
135 | case "shortcut": | ||
136 | category = ColumnCategory.Shortcut; | ||
137 | break; | ||
138 | case "template": | ||
139 | category = ColumnCategory.Template; | ||
140 | break; | ||
141 | case "text": | ||
142 | category = ColumnCategory.Text; | ||
143 | break; | ||
144 | case "timeDate": | ||
145 | category = ColumnCategory.TimeDate; | ||
146 | break; | ||
147 | case "upperCase": | ||
148 | category = ColumnCategory.UpperCase; | ||
149 | break; | ||
150 | case "version": | ||
151 | category = ColumnCategory.Version; | ||
152 | break; | ||
153 | case "wildCardFilename": | ||
154 | category = ColumnCategory.WildCardFilename; | ||
155 | break; | ||
156 | default: | ||
157 | throw new InvalidOperationException(); | ||
158 | } | ||
159 | break; | ||
160 | case "description": | ||
161 | description = reader.Value; | ||
162 | break; | ||
163 | case "keyColumn": | ||
164 | keyColumn = Convert.ToInt32(reader.Value, 10); | ||
165 | break; | ||
166 | case "keyTable": | ||
167 | keyTable = reader.Value; | ||
168 | break; | ||
169 | case "length": | ||
170 | length = Convert.ToInt32(reader.Value, 10); | ||
171 | break; | ||
172 | case "localizable": | ||
173 | localizable = reader.Value.Equals("yes"); | ||
174 | break; | ||
175 | case "maxValue": | ||
176 | maxValue = Convert.ToInt32(reader.Value, 10); | ||
177 | break; | ||
178 | case "minValue": | ||
179 | minValue = Convert.ToInt32(reader.Value, 10); | ||
180 | break; | ||
181 | case "modularize": | ||
182 | switch (reader.Value) | ||
183 | { | ||
184 | case "column": | ||
185 | modularize = ColumnModularizeType.Column; | ||
186 | break; | ||
187 | case "companionFile": | ||
188 | modularize = ColumnModularizeType.CompanionFile; | ||
189 | break; | ||
190 | case "condition": | ||
191 | modularize = ColumnModularizeType.Condition; | ||
192 | break; | ||
193 | case "controlEventArgument": | ||
194 | modularize = ColumnModularizeType.ControlEventArgument; | ||
195 | break; | ||
196 | case "controlText": | ||
197 | modularize = ColumnModularizeType.ControlText; | ||
198 | break; | ||
199 | case "icon": | ||
200 | modularize = ColumnModularizeType.Icon; | ||
201 | break; | ||
202 | case "none": | ||
203 | modularize = ColumnModularizeType.None; | ||
204 | break; | ||
205 | case "property": | ||
206 | modularize = ColumnModularizeType.Property; | ||
207 | break; | ||
208 | case "semicolonDelimited": | ||
209 | modularize = ColumnModularizeType.SemicolonDelimited; | ||
210 | break; | ||
211 | default: | ||
212 | throw new XmlException(); | ||
213 | } | ||
214 | break; | ||
215 | case "name": | ||
216 | switch (reader.Value) | ||
217 | { | ||
218 | case "CREATE": | ||
219 | case "DELETE": | ||
220 | case "DROP": | ||
221 | case "INSERT": | ||
222 | throw new XmlException(); | ||
223 | default: | ||
224 | name = reader.Value; | ||
225 | break; | ||
226 | } | ||
227 | break; | ||
228 | case "nullable": | ||
229 | nullable = reader.Value.Equals("yes"); | ||
230 | break; | ||
231 | case "primaryKey": | ||
232 | primaryKey = reader.Value.Equals("yes"); | ||
233 | break; | ||
234 | case "set": | ||
235 | possibilities = reader.Value; | ||
236 | break; | ||
237 | case "type": | ||
238 | switch (reader.Value) | ||
239 | { | ||
240 | case "localized": | ||
241 | type = ColumnType.Localized; | ||
242 | break; | ||
243 | case "number": | ||
244 | type = ColumnType.Number; | ||
245 | break; | ||
246 | case "object": | ||
247 | type = ColumnType.Object; | ||
248 | break; | ||
249 | case "string": | ||
250 | type = ColumnType.String; | ||
251 | break; | ||
252 | case "preserved": | ||
253 | type = ColumnType.Preserved; | ||
254 | break; | ||
255 | default: | ||
256 | throw new XmlException(); | ||
257 | } | ||
258 | break; | ||
259 | case "useCData": | ||
260 | useCData = reader.Value.Equals("yes"); | ||
261 | break; | ||
262 | case "unreal": | ||
263 | unreal = reader.Value.Equals("yes"); | ||
264 | break; | ||
265 | } | ||
266 | } | ||
267 | |||
268 | // parse the child elements (there should be none) | ||
269 | if (!empty) | ||
270 | { | ||
271 | bool done = false; | ||
272 | |||
273 | while (!done && reader.Read()) | ||
274 | { | ||
275 | switch (reader.NodeType) | ||
276 | { | ||
277 | case XmlNodeType.Element: | ||
278 | throw new XmlException(); | ||
279 | case XmlNodeType.EndElement: | ||
280 | done = true; | ||
281 | break; | ||
282 | } | ||
283 | } | ||
284 | |||
285 | if (!done) | ||
286 | { | ||
287 | throw new XmlException(); | ||
288 | } | ||
289 | } | ||
290 | |||
291 | WixColumnDefinition columnDefinition = new WixColumnDefinition(name, type, length, primaryKey, nullable, category, minValue, maxValue, keyTable, keyColumn, possibilities, description, modularize, localizable, useCData, unreal); | ||
292 | |||
293 | return columnDefinition; | ||
294 | } | ||
295 | } | ||
296 | } | ||
diff --git a/src/internal/TablesAndTuples/WixTableDefinition.cs b/src/internal/TablesAndTuples/WixTableDefinition.cs new file mode 100644 index 00000000..61dcbb0a --- /dev/null +++ b/src/internal/TablesAndTuples/WixTableDefinition.cs | |||
@@ -0,0 +1,169 @@ | |||
1 | using System.Collections.Generic; | ||
2 | using System.Linq; | ||
3 | using System.Xml; | ||
4 | |||
5 | namespace TablesAndSymbols | ||
6 | { | ||
7 | class WixTableDefinition | ||
8 | { | ||
9 | public WixTableDefinition(string name, IEnumerable<WixColumnDefinition> columns, bool unreal, bool symbolless, string symbolDefinitionName, bool? symbolIdIsPrimaryKey) | ||
10 | { | ||
11 | this.Name = name; | ||
12 | this.VariableName = name.Replace("_", ""); | ||
13 | this.Unreal = unreal; | ||
14 | this.Columns = columns?.ToArray(); | ||
15 | this.Symbolless = symbolless; | ||
16 | this.SymbolDefinitionName = symbolless ? null : symbolDefinitionName ?? this.VariableName; | ||
17 | this.SymbolIdIsPrimaryKey = symbolIdIsPrimaryKey ?? DeriveSymbolIdIsPrimaryKey(this.Columns); | ||
18 | } | ||
19 | |||
20 | public string Name { get; } | ||
21 | |||
22 | public string VariableName { get; } | ||
23 | |||
24 | public string SymbolDefinitionName { get; } | ||
25 | |||
26 | public bool Unreal { get; } | ||
27 | |||
28 | public WixColumnDefinition[] Columns { get; } | ||
29 | |||
30 | public bool SymbolIdIsPrimaryKey { get; } | ||
31 | |||
32 | public bool Symbolless { get; } | ||
33 | |||
34 | static WixTableDefinition Read(XmlReader reader) | ||
35 | { | ||
36 | var empty = reader.IsEmptyElement; | ||
37 | string name = null; | ||
38 | string symbolDefinitionName = null; | ||
39 | var unreal = false; | ||
40 | bool? symbolIdIsPrimaryKey = null; | ||
41 | var symbolless = false; | ||
42 | |||
43 | while (reader.MoveToNextAttribute()) | ||
44 | { | ||
45 | switch (reader.LocalName) | ||
46 | { | ||
47 | case "name": | ||
48 | name = reader.Value; | ||
49 | break; | ||
50 | case "symbolDefinitionName": | ||
51 | symbolDefinitionName = reader.Value; | ||
52 | break; | ||
53 | case "symbolIdIsPrimaryKey": | ||
54 | symbolIdIsPrimaryKey = reader.Value.Equals("yes"); | ||
55 | break; | ||
56 | case "symbolless": | ||
57 | symbolless = reader.Value.Equals("yes"); | ||
58 | break; | ||
59 | case "unreal": | ||
60 | unreal = reader.Value.Equals("yes"); | ||
61 | break; | ||
62 | } | ||
63 | } | ||
64 | |||
65 | if (null == name) | ||
66 | { | ||
67 | throw new XmlException(); | ||
68 | } | ||
69 | |||
70 | var columns = new List<WixColumnDefinition>(); | ||
71 | |||
72 | // parse the child elements | ||
73 | if (!empty) | ||
74 | { | ||
75 | var done = false; | ||
76 | |||
77 | while (!done && reader.Read()) | ||
78 | { | ||
79 | switch (reader.NodeType) | ||
80 | { | ||
81 | case XmlNodeType.Element: | ||
82 | switch (reader.LocalName) | ||
83 | { | ||
84 | case "columnDefinition": | ||
85 | var columnDefinition = WixColumnDefinition.Read(reader); | ||
86 | columns.Add(columnDefinition); | ||
87 | break; | ||
88 | default: | ||
89 | throw new XmlException(); | ||
90 | } | ||
91 | break; | ||
92 | case XmlNodeType.EndElement: | ||
93 | done = true; | ||
94 | break; | ||
95 | } | ||
96 | } | ||
97 | |||
98 | if (!done) | ||
99 | { | ||
100 | throw new XmlException(); | ||
101 | } | ||
102 | } | ||
103 | |||
104 | return new WixTableDefinition(name, columns.ToArray(), unreal, symbolless, symbolDefinitionName, symbolIdIsPrimaryKey); | ||
105 | } | ||
106 | |||
107 | static bool DeriveSymbolIdIsPrimaryKey(WixColumnDefinition[] columns) | ||
108 | { | ||
109 | return columns[0].PrimaryKey && | ||
110 | columns[0].Type == ColumnType.String && | ||
111 | columns[0].Category == ColumnCategory.Identifier && | ||
112 | !columns[0].Name.EndsWith("_") && | ||
113 | (columns.Length == 1 || !columns.Skip(1).Any(t => t.PrimaryKey)); | ||
114 | } | ||
115 | |||
116 | public static List<WixTableDefinition> LoadCollection(string inputPath) | ||
117 | { | ||
118 | using (var reader = XmlReader.Create(inputPath)) | ||
119 | { | ||
120 | reader.MoveToContent(); | ||
121 | |||
122 | if ("tableDefinitions" != reader.LocalName) | ||
123 | { | ||
124 | throw new XmlException(); | ||
125 | } | ||
126 | |||
127 | var empty = reader.IsEmptyElement; | ||
128 | var tableDefinitions = new List<WixTableDefinition>(); | ||
129 | |||
130 | while (reader.MoveToNextAttribute()) | ||
131 | { | ||
132 | } | ||
133 | |||
134 | // parse the child elements | ||
135 | if (!empty) | ||
136 | { | ||
137 | var done = false; | ||
138 | |||
139 | while (!done && reader.Read()) | ||
140 | { | ||
141 | switch (reader.NodeType) | ||
142 | { | ||
143 | case XmlNodeType.Element: | ||
144 | switch (reader.LocalName) | ||
145 | { | ||
146 | case "tableDefinition": | ||
147 | tableDefinitions.Add(WixTableDefinition.Read(reader)); | ||
148 | break; | ||
149 | default: | ||
150 | throw new XmlException(); | ||
151 | } | ||
152 | break; | ||
153 | case XmlNodeType.EndElement: | ||
154 | done = true; | ||
155 | break; | ||
156 | } | ||
157 | } | ||
158 | |||
159 | if (!done) | ||
160 | { | ||
161 | throw new XmlException(); | ||
162 | } | ||
163 | } | ||
164 | |||
165 | return tableDefinitions; | ||
166 | } | ||
167 | } | ||
168 | } | ||
169 | } | ||