aboutsummaryrefslogtreecommitdiff
path: root/src/internal/TablesAndTuples
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2021-04-22 16:36:39 -0700
committerRob Mensching <rob@firegiant.com>2021-04-22 16:50:15 -0700
commit6a24996a2e831cfe402398af65b31fb1ecd575a9 (patch)
treee377370df4bc7901745c6b5f50268fa665edb3f8 /src/internal/TablesAndTuples
parent2587a8b46b705d53156f5cd1bd866f855049081d (diff)
downloadwix-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.cs56
-rw-r--r--src/internal/TablesAndTuples/Program.cs528
-rw-r--r--src/internal/TablesAndTuples/SimpleJson.cs2127
-rw-r--r--src/internal/TablesAndTuples/TablesAndTuples.csproj8
-rw-r--r--src/internal/TablesAndTuples/WixColumnDefinition.cs296
-rw-r--r--src/internal/TablesAndTuples/WixTableDefinition.cs169
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 @@
1namespace 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 @@
1using System;
2using System.Collections.Generic;
3using System.IO;
4using System.Linq;
5using System.Text;
6using System.Text.RegularExpressions;
7using System.Xml.Linq;
8using SimpleJson;
9
10namespace 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
53using System;
54using System.CodeDom.Compiler;
55using System.Collections;
56using System.Collections.Generic;
57#if !SIMPLE_JSON_NO_LINQ_EXPRESSION
58using System.Linq.Expressions;
59#endif
60using System.ComponentModel;
61using System.Diagnostics.CodeAnalysis;
62#if SIMPLE_JSON_DYNAMIC
63using System.Dynamic;
64#endif
65using System.Globalization;
66using System.Reflection;
67using System.Runtime.Serialization;
68using System.Text;
69using SimpleJson.Reflection;
70
71// ReSharper disable LoopCanBeConvertedToQuery
72// ReSharper disable RedundantExplicitArrayCreation
73// ReSharper disable SuggestUseVarKeywordEvident
74namespace 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
487namespace 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&lt;object>) and JsonObject(IDictionary&lt;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&lt;object>, a IDictionary&lt;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&lt;string,object> / IList&lt;object> object into a JSON string
600 /// </summary>
601 /// <param name="json">A IDictionary&lt;string,object> / IList&lt;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 @@
1using System;
2using System.Xml;
3
4namespace 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 @@
1using System.Collections.Generic;
2using System.Linq;
3using System.Xml;
4
5namespace 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}