@joebigelow / wix-1 / commits / 53e87718

Support tuples from extensions and make SourcePath a path instead of string

Rob Mensching committed Dec 2, 2017 at 00:44 UTC 53e877183abe0dbbb623c39380101bc369e9f265
13 files changed +371 -77
WixToolset.Data.sln
+19 -1
@@ -1,10 +1,12 @@
1 
2 Microsoft Visual Studio Solution File, Format Version 12.00
3 # Visual Studio 15
4 -VisualStudioVersion = 15.0.26730.0
4 +VisualStudioVersion = 15.0.27004.2009
5 MinimumVisualStudioVersion = 10.0.40219.1
6 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Data", "src\WixToolset.Data\WixToolset.Data.csproj", "{73ADBD3A-8FB2-47DB-BC79-9BC61C40F2E0}"
7 EndProject
8 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolsetTest.Data", "src\test\WixToolsetTest.Data\WixToolsetTest.Data.csproj", "{6C1FA8B7-BF3C-4735-95F8-26DEEFEF00C8}"
9 +EndProject
10 Global
11 GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 Debug|Any CPU = Debug|Any CPU
@@ -33,6 +35,22 @@ Global
35 {73ADBD3A-8FB2-47DB-BC79-9BC61C40F2E0}.Release|x64.Build.0 = Release|Any CPU
36 {73ADBD3A-8FB2-47DB-BC79-9BC61C40F2E0}.Release|x86.ActiveCfg = Release|Any CPU
37 {73ADBD3A-8FB2-47DB-BC79-9BC61C40F2E0}.Release|x86.Build.0 = Release|Any CPU
38 + {6C1FA8B7-BF3C-4735-95F8-26DEEFEF00C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
39 + {6C1FA8B7-BF3C-4735-95F8-26DEEFEF00C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
40 + {6C1FA8B7-BF3C-4735-95F8-26DEEFEF00C8}.Debug|arm.ActiveCfg = Debug|Any CPU
41 + {6C1FA8B7-BF3C-4735-95F8-26DEEFEF00C8}.Debug|arm.Build.0 = Debug|Any CPU
42 + {6C1FA8B7-BF3C-4735-95F8-26DEEFEF00C8}.Debug|x64.ActiveCfg = Debug|Any CPU
43 + {6C1FA8B7-BF3C-4735-95F8-26DEEFEF00C8}.Debug|x64.Build.0 = Debug|Any CPU
44 + {6C1FA8B7-BF3C-4735-95F8-26DEEFEF00C8}.Debug|x86.ActiveCfg = Debug|Any CPU
45 + {6C1FA8B7-BF3C-4735-95F8-26DEEFEF00C8}.Debug|x86.Build.0 = Debug|Any CPU
46 + {6C1FA8B7-BF3C-4735-95F8-26DEEFEF00C8}.Release|Any CPU.ActiveCfg = Release|Any CPU
47 + {6C1FA8B7-BF3C-4735-95F8-26DEEFEF00C8}.Release|Any CPU.Build.0 = Release|Any CPU
48 + {6C1FA8B7-BF3C-4735-95F8-26DEEFEF00C8}.Release|arm.ActiveCfg = Release|Any CPU
49 + {6C1FA8B7-BF3C-4735-95F8-26DEEFEF00C8}.Release|arm.Build.0 = Release|Any CPU
50 + {6C1FA8B7-BF3C-4735-95F8-26DEEFEF00C8}.Release|x64.ActiveCfg = Release|Any CPU
51 + {6C1FA8B7-BF3C-4735-95F8-26DEEFEF00C8}.Release|x64.Build.0 = Release|Any CPU
52 + {6C1FA8B7-BF3C-4735-95F8-26DEEFEF00C8}.Release|x86.ActiveCfg = Release|Any CPU
53 + {6C1FA8B7-BF3C-4735-95F8-26DEEFEF00C8}.Release|x86.Build.0 = Release|Any CPU
54 EndGlobalSection
55 GlobalSection(SolutionProperties) = preSolution
56 HideSolutionNode = FALSE
src/Directory.Build.props
+3 -1
@@ -5,11 +5,13 @@
5 <PropertyGroup>
6 <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
7 <BaseIntermediateOutputPath>$(MSBuildThisFileDirectory)..\build\obj\$(MSBuildProjectName)\</BaseIntermediateOutputPath>
8 - <OutputPath>$(MSBuildThisFileDirectory)..\build\$(Configuration)\</OutputPath>
8 + <BaseOutputPath>$(MSBuildThisFileDirectory)..\build\$(Configuration)\</BaseOutputPath>
9 + <OutputPath>$(BaseOutputPath)</OutputPath>
10
11 <Authors>WiX Toolset Team</Authors>
12 <Company>WiX Toolset</Company>
13 <Copyright>Copyright (c) .NET Foundation and contributors. All rights reserved.</Copyright>
14 + <Product>WiX Toolset</Product>
15 </PropertyGroup>
16
17 <PropertyGroup>
src/WixToolset.Data/DictionaryExtensions.cs new
+14
@@ -0,0 +1,14 @@
1 +// 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.
2 +
3 +namespace WixToolset.Data
4 +{
5 + using System.Collections.Generic;
6 +
7 + internal static class DictionaryExtensions
8 + {
9 + public static V GetValueOrDefault<K, V>(this IDictionary<K, V> dictionary, K key, V defaultValue = default(V))
10 + {
11 + return dictionary.TryGetValue(key, out var value) ? value : defaultValue;
12 + }
13 + }
14 +}
src/WixToolset.Data/ITupleDefinitionCreator.cs
+2
@@ -4,6 +4,8 @@ namespace WixToolset.Data
4 {
5 public interface ITupleDefinitionCreator
6 {
7 + void AddCustomTupleDefinition(IntermediateTupleDefinition definition);
8 +
9 bool TryGetTupleDefinitionByName(string name, out IntermediateTupleDefinition tupleDefinition);
10 }
11 }
src/WixToolset.Data/Intermediate.cs
+127 -43
@@ -4,6 +4,7 @@ namespace WixToolset.Data
4 {
5 using System;
6 using System.Collections.Generic;
7 + using System.Linq;
8 using System.IO;
9 using SimpleJson;
10
@@ -95,9 +96,24 @@ namespace WixToolset.Data
96 /// <param name="suppressVersionCheck">Suppress checking for wix.dll version mismatches.</param>
97 /// <returns>Returns the loaded intermediate.</returns>
98 public static Intermediate Load(string path, bool suppressVersionCheck = false)
99 + {
100 + using (var stream = File.OpenRead(path))
101 + {
102 + var creator = new SimpleTupleDefinitionCreator();
103 + return Intermediate.Load(stream, path, creator, suppressVersionCheck);
104 + }
105 + }
106 +
107 + /// <summary>
108 + /// Loads an intermediate from a stream.
109 + /// </summary>
110 + /// <param name="stream">Stream to intermediate file.</param>
111 + /// <param name="suppressVersionCheck">Suppress checking for wix.dll version mismatches.</param>
112 + /// <returns>Returns the loaded intermediate.</returns>
113 + public static Intermediate Load(Stream stream, bool suppressVersionCheck = false)
114 {
115 var creator = new SimpleTupleDefinitionCreator();
100 - return Intermediate.Load(path, creator, suppressVersionCheck);
116 + return Intermediate.Load(stream, creator, suppressVersionCheck);
117 }
118
119 /// <summary>
@@ -109,51 +125,22 @@ namespace WixToolset.Data
125 /// <returns>Returns the loaded intermediate.</returns>
126 public static Intermediate Load(string path, ITupleDefinitionCreator creator, bool suppressVersionCheck = false)
127 {
112 - JsonObject jsonObject;
113 -
114 - using (FileStream stream = File.OpenRead(path))
115 - using (FileStructure fs = FileStructure.Read(stream))
116 - {
117 - if (FileFormat.WixIR != fs.FileFormat)
118 - {
119 - throw new WixUnexpectedFileFormatException(path, FileFormat.WixIR, fs.FileFormat);
120 - }
121 -
122 - var json = fs.GetData();
123 - jsonObject = SimpleJson.DeserializeObject(json) as JsonObject;
124 - }
125 -
126 - if (!suppressVersionCheck)
128 + using (var stream = File.OpenRead(path))
129 {
128 - var versionJson = jsonObject.GetValueOrDefault<string>("version");
129 -
130 - if (!Version.TryParse(versionJson, out var version) || !Intermediate.CurrentVersion.Equals(version))
131 - {
132 - throw new WixException(WixDataErrors.VersionMismatch(SourceLineNumber.CreateFromUri(path), "intermediate", versionJson, Intermediate.CurrentVersion.ToString()));
133 - }
130 + return Intermediate.Load(stream, path, creator, suppressVersionCheck);
131 }
132 + }
133
136 - var id = jsonObject.GetValueOrDefault<string>("id");
137 -
138 - var sections = new List<IntermediateSection>();
139 -
140 - var sectionsJson = jsonObject.GetValueOrDefault<JsonArray>("sections");
141 - foreach (JsonObject sectionJson in sectionsJson)
142 - {
143 - var section = IntermediateSection.Deserialize(creator, sectionJson);
144 - sections.Add(section);
145 - }
146 -
147 - var localizations = new Dictionary<string, Localization>(StringComparer.OrdinalIgnoreCase);
148 -
149 - //var localizationsJson = jsonObject.GetValueOrDefault<JsonArray>("localizations") ?? new JsonArray();
150 - //foreach (JsonObject localizationJson in localizationsJson)
151 - //{
152 - // var localization = Localization.Deserialize(localizationJson);
153 - // localizations.Add(localization.Culture, localization);
154 - //}
155 -
156 - return new Intermediate(id, sections, localizations, null);
134 + /// <summary>
135 + /// Loads an intermediate from a path on disk.
136 + /// </summary>
137 + /// <param name="stream">Stream to intermediate file.</param>
138 + /// <param name="creator">ITupleDefinitionCreator to use when reconstituting the intermediate.</param>
139 + /// <param name="suppressVersionCheck">Suppress checking for wix.dll version mismatches.</param>
140 + /// <returns>Returns the loaded intermediate.</returns>
141 + public static Intermediate Load(Stream stream, ITupleDefinitionCreator creator, bool suppressVersionCheck = false)
142 + {
143 + return Load(stream, "<unknown>", creator, suppressVersionCheck);
144 }
145
146 /// <summary>
@@ -183,6 +170,21 @@ namespace WixToolset.Data
170
171 jsonObject.Add("sections", sectionsJson);
172
173 + var customDefinitions = GetCustomDefinitionsInSections();
174 +
175 + if (customDefinitions.Count > 0)
176 + {
177 + var customDefinitionsJson = new JsonArray(customDefinitions.Count);
178 +
179 + foreach (var kvp in customDefinitions.OrderBy(d => d.Key))
180 + {
181 + var customDefinitionJson = kvp.Value.Serialize();
182 + customDefinitionsJson.Add(customDefinitionJson);
183 + }
184 +
185 + jsonObject.Add("definitions", customDefinitionsJson);
186 + }
187 +
188 //if (this.Localizations.Any())
189 //{
190 // var localizationsJson = new JsonArray();
@@ -200,6 +202,73 @@ namespace WixToolset.Data
202 }
203 }
204
205 + /// <summary>
206 + /// Loads an intermediate from a path on disk.
207 + /// </summary>
208 + /// <param name="stream">Stream to intermediate file.</param>
209 + /// <param name="path">Path name of intermediate file.</param>
210 + /// <param name="creator">ITupleDefinitionCreator to use when reconstituting the intermediate.</param>
211 + /// <param name="suppressVersionCheck">Suppress checking for wix.dll version mismatches.</param>
212 + /// <returns>Returns the loaded intermediate.</returns>
213 + internal static Intermediate Load(Stream stream, string path, ITupleDefinitionCreator creator, bool suppressVersionCheck = false)
214 + {
215 + JsonObject jsonObject;
216 +
217 + using (var fs = FileStructure.Read(stream))
218 + {
219 + if (FileFormat.WixIR != fs.FileFormat)
220 + {
221 + throw new WixUnexpectedFileFormatException(path, FileFormat.WixIR, fs.FileFormat);
222 + }
223 +
224 + var json = fs.GetData();
225 + jsonObject = SimpleJson.DeserializeObject(json) as JsonObject;
226 + }
227 +
228 + if (!suppressVersionCheck)
229 + {
230 + var versionJson = jsonObject.GetValueOrDefault<string>("version");
231 +
232 + if (!Version.TryParse(versionJson, out var version) || !Intermediate.CurrentVersion.Equals(version))
233 + {
234 + throw new WixException(WixDataErrors.VersionMismatch(SourceLineNumber.CreateFromUri(path), "intermediate", versionJson, Intermediate.CurrentVersion.ToString()));
235 + }
236 + }
237 +
238 + var definitionsJson = jsonObject.GetValueOrDefault<JsonArray>("definitions");
239 +
240 + if (definitionsJson != null)
241 + {
242 + foreach (JsonObject definitionJson in definitionsJson)
243 + {
244 + var definition = IntermediateTupleDefinition.Deserialize(definitionJson);
245 + creator.AddCustomTupleDefinition(definition);
246 + }
247 + }
248 +
249 + var id = jsonObject.GetValueOrDefault<string>("id");
250 +
251 + var sections = new List<IntermediateSection>();
252 +
253 + var sectionsJson = jsonObject.GetValueOrDefault<JsonArray>("sections");
254 + foreach (JsonObject sectionJson in sectionsJson)
255 + {
256 + var section = IntermediateSection.Deserialize(creator, sectionJson);
257 + sections.Add(section);
258 + }
259 +
260 + var localizations = new Dictionary<string, Localization>(StringComparer.OrdinalIgnoreCase);
261 +
262 + //var localizationsJson = jsonObject.GetValueOrDefault<JsonArray>("localizations") ?? new JsonArray();
263 + //foreach (JsonObject localizationJson in localizationsJson)
264 + //{
265 + // var localization = Localization.Deserialize(localizationJson);
266 + // localizations.Add(localization.Culture, localization);
267 + //}
268 +
269 + return new Intermediate(id, sections, localizations, null);
270 + }
271 +
272 #if false
273 /// <summary>
274 /// Loads an intermediate from a path on disk.
@@ -344,5 +413,20 @@ namespace WixToolset.Data
413 writer.WriteEndElement();
414 }
415 #endif
416 +
417 + private Dictionary<string, IntermediateTupleDefinition> GetCustomDefinitionsInSections()
418 + {
419 + var customDefinitions = new Dictionary<string, IntermediateTupleDefinition>();
420 +
421 + foreach (var tuple in this.Sections.SelectMany(s => s.Tuples).Where(t => t.Definition.Type == TupleDefinitionType.MustBeFromAnExtension))
422 + {
423 + if (!customDefinitions.ContainsKey(tuple.Definition.Name))
424 + {
425 + customDefinitions.Add(tuple.Definition.Name, tuple.Definition);
426 + }
427 + }
428 +
429 + return customDefinitions;
430 + }
431 }
432 }
src/WixToolset.Data/IntermediateFieldExtensions.cs
+58 -14
@@ -11,6 +11,8 @@ namespace WixToolset.Data
11
12 public static IntermediateField Set(this IntermediateField field, object value)
13 {
14 + var data = value;
15 +
16 if (field == null)
17 {
18 throw new ArgumentNullException(nameof(field));
@@ -21,30 +23,72 @@ namespace WixToolset.Data
23 }
24 else if (field.Type == IntermediateFieldType.Bool && !(value is bool))
25 {
24 - throw new ArgumentException(nameof(value));
26 + if (value is int)
27 + {
28 + data = ((int)value) != 0;
29 + }
30 + else if (value is string str)
31 + {
32 + if (str.Equals("yes", StringComparison.OrdinalIgnoreCase) || str.Equals("true", StringComparison.OrdinalIgnoreCase))
33 + {
34 + data = true;
35 + }
36 + else if (str.Equals("no", StringComparison.OrdinalIgnoreCase) || str.Equals("false", StringComparison.OrdinalIgnoreCase))
37 + {
38 + data = false;
39 + }
40 + else
41 + {
42 + throw new ArgumentException(nameof(value));
43 + }
44 + }
45 + else
46 + {
47 + throw new ArgumentException(nameof(value));
48 + }
49 }
50 else if (field.Type == IntermediateFieldType.Number && !(value is int))
51 {
28 - throw new ArgumentException(nameof(value));
52 + if (value is string str && Int32.TryParse(str, out var number))
53 + {
54 + data = number;
55 + }
56 + else
57 + {
58 + throw new ArgumentException(nameof(value));
59 + }
60 }
61 else if (field.Type == IntermediateFieldType.String && !(value is string))
62 {
32 - throw new ArgumentException(nameof(value));
33 - }
34 - else if (field.Type == IntermediateFieldType.Path && !(value is IntermediateFieldPathValue || value is string))
35 - {
36 - throw new ArgumentException(nameof(value));
37 - }
38 -
39 - if (field.Type == IntermediateFieldType.Path && value != null && value is string)
40 - {
41 - value = new IntermediateFieldPathValue { Path = (string)value };
63 + if (value is int)
64 + {
65 + data = value.ToString();
66 + }
67 + else if (value is bool b)
68 + {
69 + data = b ? "true" : "false";
70 + }
71 + else
72 + {
73 + throw new ArgumentException(nameof(value));
74 + }
75 + }
76 + else if (field.Type == IntermediateFieldType.Path && !(value is IntermediateFieldPathValue))
77 + {
78 + if (value is string str)
79 + {
80 + data = new IntermediateFieldPathValue { Path = str };
81 + }
82 + else
83 + {
84 + throw new ArgumentException(nameof(value));
85 + }
86 }
87
88 field.Value = new IntermediateFieldValue
89 {
90 Context = valueContext,
47 - Data = value,
91 + Data = data,
92 PreviousValue = field.Value
93 };
94
@@ -103,7 +147,7 @@ namespace WixToolset.Data
147 return field.Value.AsNumber() != 0;
148
149 case IntermediateFieldType.String:
106 - return !System.String.IsNullOrEmpty(field.Value.AsString());
150 + return !String.IsNullOrEmpty(field.Value.AsString());
151
152 default:
153 throw new InvalidCastException($"Cannot convert field {field.Name} with type {field.Type} to boolean");
src/WixToolset.Data/IntermediateFieldValueExtensions.cs
+71 -5
@@ -2,26 +2,71 @@
2
3 namespace WixToolset.Data
4 {
5 + using System;
6 +
7 public static class IntermediateFieldValueExtensions
8 {
9 public static bool AsBool(this IntermediateFieldValue value)
10 {
9 - return value?.Data == null ? false : (bool)value.Data;
11 + var result = value.AsNullableBool();
12 + return result.HasValue && result.Value;
13 }
14
15 public static bool? AsNullableBool(this IntermediateFieldValue value)
16 {
14 - return (bool?)value?.Data;
17 + if (value?.Data == null)
18 + {
19 + return null;
20 + }
21 + else if (value.Data is bool b)
22 + {
23 + return b;
24 + }
25 + else if (value.Data is int n)
26 + {
27 + return n != 0;
28 + }
29 + else if (value.Data is string s)
30 + {
31 + if (s.Equals("yes", StringComparison.OrdinalIgnoreCase) || s.Equals("true", StringComparison.OrdinalIgnoreCase))
32 + {
33 + return true;
34 + }
35 + else if (s.Equals("no", StringComparison.OrdinalIgnoreCase) || s.Equals("false", StringComparison.OrdinalIgnoreCase))
36 + {
37 + return false;
38 + }
39 + }
40 +
41 + return (bool)value.Data;
42 }
43
44 public static int AsNumber(this IntermediateFieldValue value)
45 {
19 - return value?.Data == null ? 0 : (int)value.Data;
46 + var result = value.AsNullableNumber();
47 + return result ?? 0;
48 }
49
50 public static int? AsNullableNumber(this IntermediateFieldValue value)
51 {
24 - return (int?)value?.Data;
52 + if (value?.Data == null)
53 + {
54 + return null;
55 + }
56 + else if (value.Data is int n)
57 + {
58 + return n;
59 + }
60 + else if (value.Data is bool b)
61 + {
62 + return b ? 1 : 0;
63 + }
64 + else if (value.Data is string s)
65 + {
66 + return Convert.ToInt32(s);
67 + }
68 +
69 + return (int)value.Data;
70 }
71
72 public static IntermediateFieldPathValue AsPath(this IntermediateFieldValue value)
@@ -31,7 +76,28 @@ namespace WixToolset.Data
76
77 public static string AsString(this IntermediateFieldValue value)
78 {
34 - return (string)value?.Data;
79 + if (value?.Data == null)
80 + {
81 + return null;
82 + }
83 + else if (value.Data is string s)
84 + {
85 + return s;
86 + }
87 + else if (value.Data is int n)
88 + {
89 + return n.ToString();
90 + }
91 + else if (value.Data is bool b)
92 + {
93 + return b ? "true" : "false";
94 + }
95 + else if (value.Data is IntermediateFieldPathValue p)
96 + {
97 + return p.Path;
98 + }
99 +
100 + return (string)value.Data;
101 }
102 }
103 }
src/WixToolset.Data/IntermediateSection.cs
+2 -1
@@ -66,7 +66,6 @@ namespace WixToolset.Data
66 var codepage = jsonObject.GetValueOrDefault("codepage", 0);
67 var id = jsonObject.GetValueOrDefault<string>("id");
68 var type = jsonObject.GetEnumOrDefault("type", SectionType.Unknown);
69 - var tuplesJson = jsonObject.GetValueOrDefault<JsonArray>("tuples");
69
70 if (null == id && (SectionType.Unknown != type && SectionType.Fragment != type))
71 {
@@ -80,6 +79,8 @@ namespace WixToolset.Data
79
80 var section = new IntermediateSection(id, type, codepage);
81
82 + var tuplesJson = jsonObject.GetValueOrDefault<JsonArray>("tuples");
83 +
84 foreach (JsonObject tupleJson in tuplesJson)
85 {
86 var tuple = IntermediateTuple.Deserialize(creator, tupleJson);
src/WixToolset.Data/IntermediateTuple.cs
+7 -7
@@ -6,9 +6,9 @@ namespace WixToolset.Data
6
7 public class IntermediateTuple
8 {
9 - //public IntermediateTuple(IntermediateTupleDefinition definition) : this(definition, null, null)
10 - //{
11 - //}
9 + public IntermediateTuple(IntermediateTupleDefinition definition) : this(definition, null, null)
10 + {
11 + }
12
13 public IntermediateTuple(IntermediateTupleDefinition definition, SourceLineNumber sourceLineNumber, Identifier id = null)
14 {
@@ -35,11 +35,11 @@ namespace WixToolset.Data
35 var sourceLineNumbersJson = jsonObject.GetValueOrDefault<JsonObject>("ln");
36 var fieldsJson = jsonObject.GetValueOrDefault<JsonArray>("fields");
37
38 - creator.TryGetTupleDefinitionByName(definitionName, out var definition); // TODO: this isn't sufficient.
39 - var tuple = definition.CreateTuple();
38 + var id = (idJson == null) ? null : Identifier.Deserialize(idJson);
39 + var sourceLineNumbers = (sourceLineNumbersJson == null) ? null : SourceLineNumber.Deserialize(sourceLineNumbersJson);
40
41 - tuple.Id = (idJson == null) ? null : Identifier.Deserialize(idJson);
42 - tuple.SourceLineNumbers = (sourceLineNumbersJson == null) ? null : SourceLineNumber.Deserialize(sourceLineNumbersJson);
41 + creator.TryGetTupleDefinitionByName(definitionName, out var definition); // TODO: this isn't sufficient.
42 + var tuple = definition.CreateTuple(sourceLineNumbers, id);
43
44 for (var i = 0; i < fieldsJson.Count; ++i)
45 {
src/WixToolset.Data/IntermediateTupleDefinition.cs
+49 -1
@@ -3,6 +3,7 @@
3 namespace WixToolset.Data
4 {
5 using System;
6 + using SimpleJson;
7
8 public class IntermediateTupleDefinition
9 {
@@ -23,7 +24,7 @@ namespace WixToolset.Data
24 this.FieldDefinitions = fieldDefinitions;
25 this.StrongTupleType = strongTupleType ?? typeof(IntermediateTuple);
26 #if DEBUG
26 - if (!this.StrongTupleType.IsSubclassOf(typeof(IntermediateTuple))) throw new ArgumentException(nameof(strongTupleType));
27 + if (this.StrongTupleType != typeof(IntermediateTuple) && !this.StrongTupleType.IsSubclassOf(typeof(IntermediateTuple))) throw new ArgumentException(nameof(strongTupleType));
28 #endif
29 }
30
@@ -43,5 +44,52 @@ namespace WixToolset.Data
44
45 return result;
46 }
47 +
48 + internal static IntermediateTupleDefinition Deserialize(JsonObject jsonObject)
49 + {
50 + var name = jsonObject.GetValueOrDefault<string>("name");
51 + var definitionsJson = jsonObject.GetValueOrDefault<JsonArray>("fields");
52 +
53 + var fieldDefinitions = new IntermediateFieldDefinition[definitionsJson.Count];
54 +
55 + for (var i = 0; i < definitionsJson.Count; ++i)
56 + {
57 + var definitionJson = (JsonObject)definitionsJson[i];
58 + var fieldName = definitionJson.GetValueOrDefault<string>("name");
59 + var fieldType = definitionJson.GetEnumOrDefault("type", IntermediateFieldType.String);
60 + fieldDefinitions[i] = new IntermediateFieldDefinition(fieldName, fieldType);
61 + }
62 +
63 + return new IntermediateTupleDefinition(name, fieldDefinitions, null);
64 + }
65 +
66 + internal JsonObject Serialize()
67 + {
68 + var jsonObject = new JsonObject
69 + {
70 + { "name", this.Name }
71 + };
72 +
73 + var fieldsJson = new JsonArray(this.FieldDefinitions.Length);
74 +
75 + foreach (var fieldDefinition in this.FieldDefinitions)
76 + {
77 + var fieldJson = new JsonObject
78 + {
79 + { "name", fieldDefinition.Name },
80 + };
81 +
82 + if (fieldDefinition.Type != IntermediateFieldType.String)
83 + {
84 + fieldJson.Add("type", fieldDefinition.Type.ToString().ToLowerInvariant());
85 + }
86 +
87 + fieldsJson.Add(fieldJson);
88 + }
89 +
90 + jsonObject.Add("fields", fieldsJson);
91 +
92 + return jsonObject;
93 + }
94 }
95 }
src/WixToolset.Data/IntermediateTupleExtensions.cs
+1 -1
@@ -8,7 +8,7 @@ namespace WixToolset.Data
8 {
9 var definition = tuple.Definition.FieldDefinitions[index];
10
11 - var field = tuple.Fields[index].Set(definition, value); ;
11 + var field = tuple.Fields[index].Set(definition, value);
12
13 return tuple.Fields[index] = field;
14 }
src/WixToolset.Data/SimpleTupleDefinitionCreator.cs
+16 -1
@@ -2,12 +2,27 @@
2
3 namespace WixToolset.Data
4 {
5 + using System.Collections.Generic;
6 +
7 internal class SimpleTupleDefinitionCreator : ITupleDefinitionCreator
8 {
9 + private Dictionary<string, IntermediateTupleDefinition> CustomDefinitionByName { get; } = new Dictionary<string, IntermediateTupleDefinition>();
10 +
11 + public void AddCustomTupleDefinition(IntermediateTupleDefinition definition)
12 + {
13 + this.CustomDefinitionByName.Add(definition.Name, definition);
14 + }
15 +
16 public bool TryGetTupleDefinitionByName(string name, out IntermediateTupleDefinition tupleDefinition)
17 {
18 tupleDefinition = TupleDefinitions.ByName(name);
19 +
20 + if (tupleDefinition == null)
21 + {
22 + tupleDefinition = this.CustomDefinitionByName.GetValueOrDefault(name);
23 + }
24 +
25 return tupleDefinition != null;
26 }
27 }
13 -}
\ No newline at end of file
28 +}
src/WixToolset.Data/Tuples/WixFileTuple.cs
+2 -2
@@ -131,9 +131,9 @@ namespace WixToolset.Data.Tuples
131 set => this.Set((int)WixFileTupleFields.DiskId, value);
132 }
133
134 - public string Source
134 + public IntermediateFieldPathValue Source
135 {
136 - get => this.Fields[(int)WixFileTupleFields.Source].AsPath()?.Path;
136 + get => this.Fields[(int)WixFileTupleFields.Source].AsPath();
137 set => this.Set((int)WixFileTupleFields.Source, value);
138 }
139