aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2017-12-02 00:44:45 -0800
committerRob Mensching <rob@firegiant.com>2017-12-02 00:44:45 -0800
commit53e877183abe0dbbb623c39380101bc369e9f265 (patch)
tree2b1a35e142d76013ba28d0a50e894ff477ee247a
parent414bf166e07703056ad186fa8ec23a4119dd9993 (diff)
downloadwix-53e877183abe0dbbb623c39380101bc369e9f265.tar.gz
wix-53e877183abe0dbbb623c39380101bc369e9f265.tar.bz2
wix-53e877183abe0dbbb623c39380101bc369e9f265.zip
Support tuples from extensions and make SourcePath a path instead of string
-rw-r--r--WixToolset.Data.sln20
-rw-r--r--src/Directory.Build.props4
-rw-r--r--src/WixToolset.Data/DictionaryExtensions.cs14
-rw-r--r--src/WixToolset.Data/ITupleDefinitionCreator.cs2
-rw-r--r--src/WixToolset.Data/Intermediate.cs170
-rw-r--r--src/WixToolset.Data/IntermediateFieldExtensions.cs72
-rw-r--r--src/WixToolset.Data/IntermediateFieldValueExtensions.cs76
-rw-r--r--src/WixToolset.Data/IntermediateSection.cs3
-rw-r--r--src/WixToolset.Data/IntermediateTuple.cs14
-rw-r--r--src/WixToolset.Data/IntermediateTupleDefinition.cs50
-rw-r--r--src/WixToolset.Data/IntermediateTupleExtensions.cs2
-rw-r--r--src/WixToolset.Data/SimpleTupleDefinitionCreator.cs17
-rw-r--r--src/WixToolset.Data/Tuples/WixFileTuple.cs4
13 files changed, 371 insertions, 77 deletions
diff --git a/WixToolset.Data.sln b/WixToolset.Data.sln
index b5e4dfc2..d8c62b1d 100644
--- a/WixToolset.Data.sln
+++ b/WixToolset.Data.sln
@@ -1,10 +1,12 @@
1 1
2Microsoft Visual Studio Solution File, Format Version 12.00 2Microsoft Visual Studio Solution File, Format Version 12.00
3# Visual Studio 15 3# Visual Studio 15
4VisualStudioVersion = 15.0.26730.0 4VisualStudioVersion = 15.0.27004.2009
5MinimumVisualStudioVersion = 10.0.40219.1 5MinimumVisualStudioVersion = 10.0.40219.1
6Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Data", "src\WixToolset.Data\WixToolset.Data.csproj", "{73ADBD3A-8FB2-47DB-BC79-9BC61C40F2E0}" 6Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Data", "src\WixToolset.Data\WixToolset.Data.csproj", "{73ADBD3A-8FB2-47DB-BC79-9BC61C40F2E0}"
7EndProject 7EndProject
8Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolsetTest.Data", "src\test\WixToolsetTest.Data\WixToolsetTest.Data.csproj", "{6C1FA8B7-BF3C-4735-95F8-26DEEFEF00C8}"
9EndProject
8Global 10Global
9 GlobalSection(SolutionConfigurationPlatforms) = preSolution 11 GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 Debug|Any CPU = Debug|Any CPU 12 Debug|Any CPU = Debug|Any CPU
@@ -33,6 +35,22 @@ Global
33 {73ADBD3A-8FB2-47DB-BC79-9BC61C40F2E0}.Release|x64.Build.0 = Release|Any CPU 35 {73ADBD3A-8FB2-47DB-BC79-9BC61C40F2E0}.Release|x64.Build.0 = Release|Any CPU
34 {73ADBD3A-8FB2-47DB-BC79-9BC61C40F2E0}.Release|x86.ActiveCfg = Release|Any CPU 36 {73ADBD3A-8FB2-47DB-BC79-9BC61C40F2E0}.Release|x86.ActiveCfg = Release|Any CPU
35 {73ADBD3A-8FB2-47DB-BC79-9BC61C40F2E0}.Release|x86.Build.0 = 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
36 EndGlobalSection 54 EndGlobalSection
37 GlobalSection(SolutionProperties) = preSolution 55 GlobalSection(SolutionProperties) = preSolution
38 HideSolutionNode = FALSE 56 HideSolutionNode = FALSE
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 25cb6d36..7cd6767f 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -5,11 +5,13 @@
5 <PropertyGroup> 5 <PropertyGroup>
6 <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> 6 <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
7 <BaseIntermediateOutputPath>$(MSBuildThisFileDirectory)..\build\obj\$(MSBuildProjectName)\</BaseIntermediateOutputPath> 7 <BaseIntermediateOutputPath>$(MSBuildThisFileDirectory)..\build\obj\$(MSBuildProjectName)\</BaseIntermediateOutputPath>
8 <OutputPath>$(MSBuildThisFileDirectory)..\build\$(Configuration)\</OutputPath> 8 <BaseOutputPath>$(MSBuildThisFileDirectory)..\build\$(Configuration)\</BaseOutputPath>
9 <OutputPath>$(BaseOutputPath)</OutputPath>
9 10
10 <Authors>WiX Toolset Team</Authors> 11 <Authors>WiX Toolset Team</Authors>
11 <Company>WiX Toolset</Company> 12 <Company>WiX Toolset</Company>
12 <Copyright>Copyright (c) .NET Foundation and contributors. All rights reserved.</Copyright> 13 <Copyright>Copyright (c) .NET Foundation and contributors. All rights reserved.</Copyright>
14 <Product>WiX Toolset</Product>
13 </PropertyGroup> 15 </PropertyGroup>
14 16
15 <PropertyGroup> 17 <PropertyGroup>
diff --git a/src/WixToolset.Data/DictionaryExtensions.cs b/src/WixToolset.Data/DictionaryExtensions.cs
new file mode 100644
index 00000000..cb6647ff
--- /dev/null
+++ b/src/WixToolset.Data/DictionaryExtensions.cs
@@ -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
3namespace 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}
diff --git a/src/WixToolset.Data/ITupleDefinitionCreator.cs b/src/WixToolset.Data/ITupleDefinitionCreator.cs
index 63477314..7d818be1 100644
--- a/src/WixToolset.Data/ITupleDefinitionCreator.cs
+++ b/src/WixToolset.Data/ITupleDefinitionCreator.cs
@@ -4,6 +4,8 @@ namespace WixToolset.Data
4{ 4{
5 public interface ITupleDefinitionCreator 5 public interface ITupleDefinitionCreator
6 { 6 {
7 void AddCustomTupleDefinition(IntermediateTupleDefinition definition);
8
7 bool TryGetTupleDefinitionByName(string name, out IntermediateTupleDefinition tupleDefinition); 9 bool TryGetTupleDefinitionByName(string name, out IntermediateTupleDefinition tupleDefinition);
8 } 10 }
9} 11}
diff --git a/src/WixToolset.Data/Intermediate.cs b/src/WixToolset.Data/Intermediate.cs
index b03492ce..4d4e17cc 100644
--- a/src/WixToolset.Data/Intermediate.cs
+++ b/src/WixToolset.Data/Intermediate.cs
@@ -4,6 +4,7 @@ namespace WixToolset.Data
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
7 using System.Linq;
7 using System.IO; 8 using System.IO;
8 using SimpleJson; 9 using SimpleJson;
9 10
@@ -96,8 +97,23 @@ namespace WixToolset.Data
96 /// <returns>Returns the loaded intermediate.</returns> 97 /// <returns>Returns the loaded intermediate.</returns>
97 public static Intermediate Load(string path, bool suppressVersionCheck = false) 98 public static Intermediate Load(string path, bool suppressVersionCheck = false)
98 { 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 {
99 var creator = new SimpleTupleDefinitionCreator(); 115 var creator = new SimpleTupleDefinitionCreator();
100 return Intermediate.Load(path, creator, suppressVersionCheck); 116 return Intermediate.Load(stream, creator, suppressVersionCheck);
101 } 117 }
102 118
103 /// <summary> 119 /// <summary>
@@ -109,51 +125,22 @@ namespace WixToolset.Data
109 /// <returns>Returns the loaded intermediate.</returns> 125 /// <returns>Returns the loaded intermediate.</returns>
110 public static Intermediate Load(string path, ITupleDefinitionCreator creator, bool suppressVersionCheck = false) 126 public static Intermediate Load(string path, ITupleDefinitionCreator creator, bool suppressVersionCheck = false)
111 { 127 {
112 JsonObject jsonObject; 128 using (var stream = File.OpenRead(path))
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)
127 { 129 {
128 var versionJson = jsonObject.GetValueOrDefault<string>("version"); 130 return Intermediate.Load(stream, path, creator, suppressVersionCheck);
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 }
134 } 131 }
132 }
135 133
136 var id = jsonObject.GetValueOrDefault<string>("id"); 134 /// <summary>
137 135 /// Loads an intermediate from a path on disk.
138 var sections = new List<IntermediateSection>(); 136 /// </summary>
139 137 /// <param name="stream">Stream to intermediate file.</param>
140 var sectionsJson = jsonObject.GetValueOrDefault<JsonArray>("sections"); 138 /// <param name="creator">ITupleDefinitionCreator to use when reconstituting the intermediate.</param>
141 foreach (JsonObject sectionJson in sectionsJson) 139 /// <param name="suppressVersionCheck">Suppress checking for wix.dll version mismatches.</param>
142 { 140 /// <returns>Returns the loaded intermediate.</returns>
143 var section = IntermediateSection.Deserialize(creator, sectionJson); 141 public static Intermediate Load(Stream stream, ITupleDefinitionCreator creator, bool suppressVersionCheck = false)
144 sections.Add(section); 142 {
145 } 143 return Load(stream, "<unknown>", creator, suppressVersionCheck);
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);
157 } 144 }
158 145
159 /// <summary> 146 /// <summary>
@@ -183,6 +170,21 @@ namespace WixToolset.Data
183 170
184 jsonObject.Add("sections", sectionsJson); 171 jsonObject.Add("sections", sectionsJson);
185 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
186 //if (this.Localizations.Any()) 188 //if (this.Localizations.Any())
187 //{ 189 //{
188 // var localizationsJson = new JsonArray(); 190 // var localizationsJson = new JsonArray();
@@ -200,6 +202,73 @@ namespace WixToolset.Data
200 } 202 }
201 } 203 }
202 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
203#if false 272#if false
204 /// <summary> 273 /// <summary>
205 /// Loads an intermediate from a path on disk. 274 /// Loads an intermediate from a path on disk.
@@ -344,5 +413,20 @@ namespace WixToolset.Data
344 writer.WriteEndElement(); 413 writer.WriteEndElement();
345 } 414 }
346#endif 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 }
347 } 431 }
348} 432}
diff --git a/src/WixToolset.Data/IntermediateFieldExtensions.cs b/src/WixToolset.Data/IntermediateFieldExtensions.cs
index c551b455..be225452 100644
--- a/src/WixToolset.Data/IntermediateFieldExtensions.cs
+++ b/src/WixToolset.Data/IntermediateFieldExtensions.cs
@@ -11,6 +11,8 @@ namespace WixToolset.Data
11 11
12 public static IntermediateField Set(this IntermediateField field, object value) 12 public static IntermediateField Set(this IntermediateField field, object value)
13 { 13 {
14 var data = value;
15
14 if (field == null) 16 if (field == null)
15 { 17 {
16 throw new ArgumentNullException(nameof(field)); 18 throw new ArgumentNullException(nameof(field));
@@ -21,30 +23,72 @@ namespace WixToolset.Data
21 } 23 }
22 else if (field.Type == IntermediateFieldType.Bool && !(value is bool)) 24 else if (field.Type == IntermediateFieldType.Bool && !(value is bool))
23 { 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 }
25 } 49 }
26 else if (field.Type == IntermediateFieldType.Number && !(value is int)) 50 else if (field.Type == IntermediateFieldType.Number && !(value is int))
27 { 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 }
29 } 60 }
30 else if (field.Type == IntermediateFieldType.String && !(value is string)) 61 else if (field.Type == IntermediateFieldType.String && !(value is string))
31 { 62 {
32 throw new ArgumentException(nameof(value)); 63 if (value is int)
33 } 64 {
34 else if (field.Type == IntermediateFieldType.Path && !(value is IntermediateFieldPathValue || value is string)) 65 data = value.ToString();
35 { 66 }
36 throw new ArgumentException(nameof(value)); 67 else if (value is bool b)
37 } 68 {
38 69 data = b ? "true" : "false";
39 if (field.Type == IntermediateFieldType.Path && value != null && value is string) 70 }
40 { 71 else
41 value = new IntermediateFieldPathValue { Path = (string)value }; 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 }
42 } 86 }
43 87
44 field.Value = new IntermediateFieldValue 88 field.Value = new IntermediateFieldValue
45 { 89 {
46 Context = valueContext, 90 Context = valueContext,
47 Data = value, 91 Data = data,
48 PreviousValue = field.Value 92 PreviousValue = field.Value
49 }; 93 };
50 94
@@ -103,7 +147,7 @@ namespace WixToolset.Data
103 return field.Value.AsNumber() != 0; 147 return field.Value.AsNumber() != 0;
104 148
105 case IntermediateFieldType.String: 149 case IntermediateFieldType.String:
106 return !System.String.IsNullOrEmpty(field.Value.AsString()); 150 return !String.IsNullOrEmpty(field.Value.AsString());
107 151
108 default: 152 default:
109 throw new InvalidCastException($"Cannot convert field {field.Name} with type {field.Type} to boolean"); 153 throw new InvalidCastException($"Cannot convert field {field.Name} with type {field.Type} to boolean");
diff --git a/src/WixToolset.Data/IntermediateFieldValueExtensions.cs b/src/WixToolset.Data/IntermediateFieldValueExtensions.cs
index bc106d4b..9a4237d8 100644
--- a/src/WixToolset.Data/IntermediateFieldValueExtensions.cs
+++ b/src/WixToolset.Data/IntermediateFieldValueExtensions.cs
@@ -2,26 +2,71 @@
2 2
3namespace WixToolset.Data 3namespace WixToolset.Data
4{ 4{
5 using System;
6
5 public static class IntermediateFieldValueExtensions 7 public static class IntermediateFieldValueExtensions
6 { 8 {
7 public static bool AsBool(this IntermediateFieldValue value) 9 public static bool AsBool(this IntermediateFieldValue value)
8 { 10 {
9 return value?.Data == null ? false : (bool)value.Data; 11 var result = value.AsNullableBool();
12 return result.HasValue && result.Value;
10 } 13 }
11 14
12 public static bool? AsNullableBool(this IntermediateFieldValue value) 15 public static bool? AsNullableBool(this IntermediateFieldValue value)
13 { 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;
15 } 42 }
16 43
17 public static int AsNumber(this IntermediateFieldValue value) 44 public static int AsNumber(this IntermediateFieldValue value)
18 { 45 {
19 return value?.Data == null ? 0 : (int)value.Data; 46 var result = value.AsNullableNumber();
47 return result ?? 0;
20 } 48 }
21 49
22 public static int? AsNullableNumber(this IntermediateFieldValue value) 50 public static int? AsNullableNumber(this IntermediateFieldValue value)
23 { 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;
25 } 70 }
26 71
27 public static IntermediateFieldPathValue AsPath(this IntermediateFieldValue value) 72 public static IntermediateFieldPathValue AsPath(this IntermediateFieldValue value)
@@ -31,7 +76,28 @@ namespace WixToolset.Data
31 76
32 public static string AsString(this IntermediateFieldValue value) 77 public static string AsString(this IntermediateFieldValue value)
33 { 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;
35 } 101 }
36 } 102 }
37} 103}
diff --git a/src/WixToolset.Data/IntermediateSection.cs b/src/WixToolset.Data/IntermediateSection.cs
index 2b1f7375..5d17eb31 100644
--- a/src/WixToolset.Data/IntermediateSection.cs
+++ b/src/WixToolset.Data/IntermediateSection.cs
@@ -66,7 +66,6 @@ namespace WixToolset.Data
66 var codepage = jsonObject.GetValueOrDefault("codepage", 0); 66 var codepage = jsonObject.GetValueOrDefault("codepage", 0);
67 var id = jsonObject.GetValueOrDefault<string>("id"); 67 var id = jsonObject.GetValueOrDefault<string>("id");
68 var type = jsonObject.GetEnumOrDefault("type", SectionType.Unknown); 68 var type = jsonObject.GetEnumOrDefault("type", SectionType.Unknown);
69 var tuplesJson = jsonObject.GetValueOrDefault<JsonArray>("tuples");
70 69
71 if (null == id && (SectionType.Unknown != type && SectionType.Fragment != type)) 70 if (null == id && (SectionType.Unknown != type && SectionType.Fragment != type))
72 { 71 {
@@ -80,6 +79,8 @@ namespace WixToolset.Data
80 79
81 var section = new IntermediateSection(id, type, codepage); 80 var section = new IntermediateSection(id, type, codepage);
82 81
82 var tuplesJson = jsonObject.GetValueOrDefault<JsonArray>("tuples");
83
83 foreach (JsonObject tupleJson in tuplesJson) 84 foreach (JsonObject tupleJson in tuplesJson)
84 { 85 {
85 var tuple = IntermediateTuple.Deserialize(creator, tupleJson); 86 var tuple = IntermediateTuple.Deserialize(creator, tupleJson);
diff --git a/src/WixToolset.Data/IntermediateTuple.cs b/src/WixToolset.Data/IntermediateTuple.cs
index 0f1e5965..cda133b5 100644
--- a/src/WixToolset.Data/IntermediateTuple.cs
+++ b/src/WixToolset.Data/IntermediateTuple.cs
@@ -6,9 +6,9 @@ namespace WixToolset.Data
6 6
7 public class IntermediateTuple 7 public class IntermediateTuple
8 { 8 {
9 //public IntermediateTuple(IntermediateTupleDefinition definition) : this(definition, null, null) 9 public IntermediateTuple(IntermediateTupleDefinition definition) : this(definition, null, null)
10 //{ 10 {
11 //} 11 }
12 12
13 public IntermediateTuple(IntermediateTupleDefinition definition, SourceLineNumber sourceLineNumber, Identifier id = null) 13 public IntermediateTuple(IntermediateTupleDefinition definition, SourceLineNumber sourceLineNumber, Identifier id = null)
14 { 14 {
@@ -35,11 +35,11 @@ namespace WixToolset.Data
35 var sourceLineNumbersJson = jsonObject.GetValueOrDefault<JsonObject>("ln"); 35 var sourceLineNumbersJson = jsonObject.GetValueOrDefault<JsonObject>("ln");
36 var fieldsJson = jsonObject.GetValueOrDefault<JsonArray>("fields"); 36 var fieldsJson = jsonObject.GetValueOrDefault<JsonArray>("fields");
37 37
38 creator.TryGetTupleDefinitionByName(definitionName, out var definition); // TODO: this isn't sufficient. 38 var id = (idJson == null) ? null : Identifier.Deserialize(idJson);
39 var tuple = definition.CreateTuple(); 39 var sourceLineNumbers = (sourceLineNumbersJson == null) ? null : SourceLineNumber.Deserialize(sourceLineNumbersJson);
40 40
41 tuple.Id = (idJson == null) ? null : Identifier.Deserialize(idJson); 41 creator.TryGetTupleDefinitionByName(definitionName, out var definition); // TODO: this isn't sufficient.
42 tuple.SourceLineNumbers = (sourceLineNumbersJson == null) ? null : SourceLineNumber.Deserialize(sourceLineNumbersJson); 42 var tuple = definition.CreateTuple(sourceLineNumbers, id);
43 43
44 for (var i = 0; i < fieldsJson.Count; ++i) 44 for (var i = 0; i < fieldsJson.Count; ++i)
45 { 45 {
diff --git a/src/WixToolset.Data/IntermediateTupleDefinition.cs b/src/WixToolset.Data/IntermediateTupleDefinition.cs
index 5658cfe9..eb4ab12e 100644
--- a/src/WixToolset.Data/IntermediateTupleDefinition.cs
+++ b/src/WixToolset.Data/IntermediateTupleDefinition.cs
@@ -3,6 +3,7 @@
3namespace WixToolset.Data 3namespace WixToolset.Data
4{ 4{
5 using System; 5 using System;
6 using SimpleJson;
6 7
7 public class IntermediateTupleDefinition 8 public class IntermediateTupleDefinition
8 { 9 {
@@ -23,7 +24,7 @@ namespace WixToolset.Data
23 this.FieldDefinitions = fieldDefinitions; 24 this.FieldDefinitions = fieldDefinitions;
24 this.StrongTupleType = strongTupleType ?? typeof(IntermediateTuple); 25 this.StrongTupleType = strongTupleType ?? typeof(IntermediateTuple);
25#if DEBUG 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));
27#endif 28#endif
28 } 29 }
29 30
@@ -43,5 +44,52 @@ namespace WixToolset.Data
43 44
44 return result; 45 return result;
45 } 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 }
46 } 94 }
47} 95}
diff --git a/src/WixToolset.Data/IntermediateTupleExtensions.cs b/src/WixToolset.Data/IntermediateTupleExtensions.cs
index 9865c06a..615c21f9 100644
--- a/src/WixToolset.Data/IntermediateTupleExtensions.cs
+++ b/src/WixToolset.Data/IntermediateTupleExtensions.cs
@@ -8,7 +8,7 @@ namespace WixToolset.Data
8 { 8 {
9 var definition = tuple.Definition.FieldDefinitions[index]; 9 var definition = tuple.Definition.FieldDefinitions[index];
10 10
11 var field = tuple.Fields[index].Set(definition, value); ; 11 var field = tuple.Fields[index].Set(definition, value);
12 12
13 return tuple.Fields[index] = field; 13 return tuple.Fields[index] = field;
14 } 14 }
diff --git a/src/WixToolset.Data/SimpleTupleDefinitionCreator.cs b/src/WixToolset.Data/SimpleTupleDefinitionCreator.cs
index b9d0b620..6a86639a 100644
--- a/src/WixToolset.Data/SimpleTupleDefinitionCreator.cs
+++ b/src/WixToolset.Data/SimpleTupleDefinitionCreator.cs
@@ -2,12 +2,27 @@
2 2
3namespace WixToolset.Data 3namespace WixToolset.Data
4{ 4{
5 using System.Collections.Generic;
6
5 internal class SimpleTupleDefinitionCreator : ITupleDefinitionCreator 7 internal class SimpleTupleDefinitionCreator : ITupleDefinitionCreator
6 { 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
7 public bool TryGetTupleDefinitionByName(string name, out IntermediateTupleDefinition tupleDefinition) 16 public bool TryGetTupleDefinitionByName(string name, out IntermediateTupleDefinition tupleDefinition)
8 { 17 {
9 tupleDefinition = TupleDefinitions.ByName(name); 18 tupleDefinition = TupleDefinitions.ByName(name);
19
20 if (tupleDefinition == null)
21 {
22 tupleDefinition = this.CustomDefinitionByName.GetValueOrDefault(name);
23 }
24
10 return tupleDefinition != null; 25 return tupleDefinition != null;
11 } 26 }
12 } 27 }
13} \ No newline at end of file 28}
diff --git a/src/WixToolset.Data/Tuples/WixFileTuple.cs b/src/WixToolset.Data/Tuples/WixFileTuple.cs
index 88dc4c82..3581e2e6 100644
--- a/src/WixToolset.Data/Tuples/WixFileTuple.cs
+++ b/src/WixToolset.Data/Tuples/WixFileTuple.cs
@@ -131,9 +131,9 @@ namespace WixToolset.Data.Tuples
131 set => this.Set((int)WixFileTupleFields.DiskId, value); 131 set => this.Set((int)WixFileTupleFields.DiskId, value);
132 } 132 }
133 133
134 public string Source 134 public IntermediateFieldPathValue Source
135 { 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); 137 set => this.Set((int)WixFileTupleFields.Source, value);
138 } 138 }
139 139