aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Data/Intermediate.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/WixToolset.Data/Intermediate.cs')
-rw-r--r--src/WixToolset.Data/Intermediate.cs301
1 files changed, 102 insertions, 199 deletions
diff --git a/src/WixToolset.Data/Intermediate.cs b/src/WixToolset.Data/Intermediate.cs
index 7693b815..1db21d85 100644
--- a/src/WixToolset.Data/Intermediate.cs
+++ b/src/WixToolset.Data/Intermediate.cs
@@ -14,8 +14,8 @@ namespace WixToolset.Data
14 /// </summary> 14 /// </summary>
15 public sealed class Intermediate 15 public sealed class Intermediate
16 { 16 {
17 public const string XmlNamespaceUri = "http://wixtoolset.org/schemas/v4/wixobj";
18 private static readonly Version CurrentVersion = new Version("4.0.0.0"); 17 private static readonly Version CurrentVersion = new Version("4.0.0.0");
18 private const string WixOutputStreamName = "wix-ir.json";
19 19
20 private readonly Dictionary<string, Localization> localizationsByCulture; 20 private readonly Dictionary<string, Localization> localizationsByCulture;
21 21
@@ -25,15 +25,13 @@ namespace WixToolset.Data
25 public Intermediate() 25 public Intermediate()
26 { 26 {
27 this.Id = Convert.ToBase64String(Guid.NewGuid().ToByteArray()).TrimEnd('=').Replace('+', '.').Replace('/', '_'); 27 this.Id = Convert.ToBase64String(Guid.NewGuid().ToByteArray()).TrimEnd('=').Replace('+', '.').Replace('/', '_');
28 this.EmbedFilePaths = new List<string>();
29 this.localizationsByCulture = new Dictionary<string, Localization>(StringComparer.OrdinalIgnoreCase); 28 this.localizationsByCulture = new Dictionary<string, Localization>(StringComparer.OrdinalIgnoreCase);
30 this.Sections = new List<IntermediateSection>(); 29 this.Sections = new List<IntermediateSection>();
31 } 30 }
32 31
33 public Intermediate(string id, IEnumerable<IntermediateSection> sections, IDictionary<string, Localization> localizationsByCulture, IEnumerable<string> embedFilePaths) 32 public Intermediate(string id, IEnumerable<IntermediateSection> sections, IDictionary<string, Localization> localizationsByCulture)
34 { 33 {
35 this.Id = id; 34 this.Id = id;
36 this.EmbedFilePaths = (embedFilePaths != null) ? new List<string>(embedFilePaths) : new List<string>();
37 this.localizationsByCulture = (localizationsByCulture != null) ? new Dictionary<string, Localization>(localizationsByCulture, StringComparer.OrdinalIgnoreCase) : new Dictionary<string, Localization>(StringComparer.OrdinalIgnoreCase); 35 this.localizationsByCulture = (localizationsByCulture != null) ? new Dictionary<string, Localization>(localizationsByCulture, StringComparer.OrdinalIgnoreCase) : new Dictionary<string, Localization>(StringComparer.OrdinalIgnoreCase);
38 this.Sections = (sections != null) ? new List<IntermediateSection>(sections) : new List<IntermediateSection>(); 36 this.Sections = (sections != null) ? new List<IntermediateSection>(sections) : new List<IntermediateSection>();
39 } 37 }
@@ -44,11 +42,6 @@ namespace WixToolset.Data
44 public string Id { get; } 42 public string Id { get; }
45 43
46 /// <summary> 44 /// <summary>
47 /// Get the embed file paths in this intermediate.
48 /// </summary>
49 public IList<string> EmbedFilePaths { get; }
50
51 /// <summary>
52 /// Get the localizations contained in this intermediate. 45 /// Get the localizations contained in this intermediate.
53 /// </summary> 46 /// </summary>
54 public IEnumerable<Localization> Localizations => this.localizationsByCulture.Values; 47 public IEnumerable<Localization> Localizations => this.localizationsByCulture.Values;
@@ -66,12 +59,8 @@ namespace WixToolset.Data
66 /// <returns>Returns the loaded intermediate.</returns> 59 /// <returns>Returns the loaded intermediate.</returns>
67 public static Intermediate Load(string path, bool suppressVersionCheck = false) 60 public static Intermediate Load(string path, bool suppressVersionCheck = false)
68 { 61 {
69 using (var stream = File.OpenRead(path)) 62 var creator = new SimpleTupleDefinitionCreator();
70 { 63 return Intermediate.Load(path, creator, suppressVersionCheck);
71 var uri = new Uri(Path.GetFullPath(path));
72 var creator = new SimpleTupleDefinitionCreator();
73 return Intermediate.LoadIntermediate(stream, uri, creator, suppressVersionCheck);
74 }
75 } 64 }
76 65
77 /// <summary> 66 /// <summary>
@@ -97,13 +86,9 @@ namespace WixToolset.Data
97 /// <returns>Returns the loaded intermediate.</returns> 86 /// <returns>Returns the loaded intermediate.</returns>
98 public static Intermediate Load(Assembly assembly, string resourceName, ITupleDefinitionCreator creator, bool suppressVersionCheck = false) 87 public static Intermediate Load(Assembly assembly, string resourceName, ITupleDefinitionCreator creator, bool suppressVersionCheck = false)
99 { 88 {
100 using (var resourceStream = assembly.GetManifestResourceStream(resourceName)) 89 using (var wixout = WixOutput.Read(assembly, resourceName))
101 { 90 {
102 var uriBuilder = new UriBuilder(assembly.CodeBase); 91 return Intermediate.LoadIntermediate(wixout, creator, suppressVersionCheck);
103 uriBuilder.Scheme = "embeddedresource";
104 uriBuilder.Fragment = resourceName;
105
106 return Intermediate.LoadIntermediate(resourceStream, uriBuilder.Uri, creator, suppressVersionCheck);
107 } 92 }
108 } 93 }
109 94
@@ -116,11 +101,9 @@ namespace WixToolset.Data
116 /// <returns>Returns the loaded intermediate.</returns> 101 /// <returns>Returns the loaded intermediate.</returns>
117 public static Intermediate Load(string path, ITupleDefinitionCreator creator, bool suppressVersionCheck = false) 102 public static Intermediate Load(string path, ITupleDefinitionCreator creator, bool suppressVersionCheck = false)
118 { 103 {
119 using (var stream = File.OpenRead(path)) 104 using (var wixout = WixOutput.Read(path))
120 { 105 {
121 var uri = new Uri(Path.GetFullPath(path)); 106 return Intermediate.LoadIntermediate(wixout, creator, suppressVersionCheck);
122
123 return Intermediate.LoadIntermediate(stream, uri, creator, suppressVersionCheck);
124 } 107 }
125 } 108 }
126 109
@@ -133,7 +116,6 @@ namespace WixToolset.Data
133 public static IEnumerable<Intermediate> Load(IEnumerable<string> intermediateFiles) 116 public static IEnumerable<Intermediate> Load(IEnumerable<string> intermediateFiles)
134 { 117 {
135 var creator = new SimpleTupleDefinitionCreator(); 118 var creator = new SimpleTupleDefinitionCreator();
136
137 return Intermediate.Load(intermediateFiles, creator); 119 return Intermediate.Load(intermediateFiles, creator);
138 } 120 }
139 121
@@ -151,15 +133,14 @@ namespace WixToolset.Data
151 133
152 foreach (var path in intermediateFiles) 134 foreach (var path in intermediateFiles)
153 { 135 {
154 using (var stream = File.OpenRead(path)) 136 using (var wixout = WixOutput.Read(path))
155 { 137 {
156 var uri = new Uri(Path.GetFullPath(path)); 138 var data = wixout.GetData(WixOutputStreamName);
157 139 var json = Intermediate.LoadJson(data, wixout.Uri, suppressVersionCheck);
158 var json = Intermediate.LoadJson(stream, uri, suppressVersionCheck);
159 140
160 Intermediate.LoadDefinitions(json, creator); 141 Intermediate.LoadDefinitions(json, creator);
161 142
162 jsons.Enqueue(new JsonWithPath { Json = json, Path = uri }); 143 jsons.Enqueue(new JsonWithPath { Json = json, Path = wixout.Uri });
163 } 144 }
164 } 145 }
165 146
@@ -183,55 +164,21 @@ namespace WixToolset.Data
183 { 164 {
184 Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(path))); 165 Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(path)));
185 166
186 using (var stream = File.Create(path)) 167 using (var wixout = WixOutput.Create(path))
187 using (var fs = FileStructure.Create(stream, FileFormat.WixIR, this.EmbedFilePaths))
188 using (var writer = new StreamWriter(fs.GetDataStream()))
189 { 168 {
190 var jsonObject = new JsonObject 169 this.Save(wixout);
191 { 170 }
192 { "id", this.Id }, 171 }
193 { "version", Intermediate.CurrentVersion.ToString() }
194 };
195
196 var sectionsJson = new JsonArray(this.Sections.Count);
197 foreach (var section in this.Sections)
198 {
199 var sectionJson = section.Serialize();
200 sectionsJson.Add(sectionJson);
201 }
202
203 jsonObject.Add("sections", sectionsJson);
204
205 var customDefinitions = this.GetCustomDefinitionsInSections();
206
207 if (customDefinitions.Count > 0)
208 {
209 var customDefinitionsJson = new JsonArray(customDefinitions.Count);
210
211 foreach (var kvp in customDefinitions.OrderBy(d => d.Key))
212 {
213 var customDefinitionJson = kvp.Value.Serialize();
214 customDefinitionsJson.Add(customDefinitionJson);
215 }
216
217 jsonObject.Add("definitions", customDefinitionsJson);
218 }
219
220 if (this.Localizations.Any())
221 {
222 var localizationsJson = new JsonArray();
223 foreach (var localization in this.Localizations)
224 {
225 var localizationJson = localization.Serialize();
226 localizationsJson.Add(localizationJson);
227 }
228 172
229 jsonObject.Add("localizations", localizationsJson); 173 /// <summary>
230 } 174 /// Saves an intermediate to a path on disk.
175 /// </summary>
176 /// <param name="path">Path to save intermediate file to disk.</param>
177 public void Save(WixOutput wixout)
178 {
179 this.SaveEmbedFiles(wixout);
231 180
232 var json = SimpleJson.SerializeObject(jsonObject); 181 this.SaveIR(wixout);
233 writer.Write(json);
234 }
235 } 182 }
236 183
237 /// <summary> 184 /// <summary>
@@ -242,13 +189,14 @@ namespace WixToolset.Data
242 /// <param name="creator">ITupleDefinitionCreator to use when reconstituting the intermediate.</param> 189 /// <param name="creator">ITupleDefinitionCreator to use when reconstituting the intermediate.</param>
243 /// <param name="suppressVersionCheck">Suppress checking for wix.dll version mismatches.</param> 190 /// <param name="suppressVersionCheck">Suppress checking for wix.dll version mismatches.</param>
244 /// <returns>Returns the loaded intermediate.</returns> 191 /// <returns>Returns the loaded intermediate.</returns>
245 private static Intermediate LoadIntermediate(Stream stream, Uri baseUri, ITupleDefinitionCreator creator, bool suppressVersionCheck = false) 192 private static Intermediate LoadIntermediate(WixOutput wixout, ITupleDefinitionCreator creator, bool suppressVersionCheck = false)
246 { 193 {
247 var json = Intermediate.LoadJson(stream, baseUri, suppressVersionCheck); 194 var data = wixout.GetData(WixOutputStreamName);
195 var json = Intermediate.LoadJson(data, wixout.Uri, suppressVersionCheck);
248 196
249 Intermediate.LoadDefinitions(json, creator); 197 Intermediate.LoadDefinitions(json, creator);
250 198
251 return Intermediate.FinalizeLoad(json, baseUri, creator); 199 return Intermediate.FinalizeLoad(json, wixout.Uri, creator);
252 } 200 }
253 201
254 /// <summary> 202 /// <summary>
@@ -258,19 +206,9 @@ namespace WixToolset.Data
258 /// <param name="baseUri">Path name of intermediate file.</param> 206 /// <param name="baseUri">Path name of intermediate file.</param>
259 /// <param name="suppressVersionCheck">Suppress checking for wix.dll version mismatches.</param> 207 /// <param name="suppressVersionCheck">Suppress checking for wix.dll version mismatches.</param>
260 /// <returns>Returns the loaded json.</returns> 208 /// <returns>Returns the loaded json.</returns>
261 private static JsonObject LoadJson(Stream stream, Uri baseUri, bool suppressVersionCheck) 209 private static JsonObject LoadJson(string json, Uri baseUri, bool suppressVersionCheck)
262 { 210 {
263 JsonObject jsonObject; 211 var jsonObject = SimpleJson.DeserializeObject(json) as JsonObject;
264 using (var fs = FileStructure.Read(stream))
265 {
266 if (FileFormat.WixIR != fs.FileFormat)
267 {
268 throw new WixUnexpectedFileFormatException(baseUri.LocalPath, FileFormat.WixIR, fs.FileFormat);
269 }
270
271 var json = fs.GetData();
272 jsonObject = SimpleJson.DeserializeObject(json) as JsonObject;
273 }
274 212
275 if (!suppressVersionCheck) 213 if (!suppressVersionCheck)
276 { 214 {
@@ -333,153 +271,118 @@ namespace WixToolset.Data
333 localizations.Add(localization.Culture, localization); 271 localizations.Add(localization.Culture, localization);
334 } 272 }
335 273
336 return new Intermediate(id, sections, localizations, null); 274 return new Intermediate(id, sections, localizations);
337 } 275 }
338 276
339#if false 277 private void SaveEmbedFiles(WixOutput wixout)
340 /// <summary>
341 /// Loads an intermediate from a path on disk.
342 /// </summary>
343 /// <param name="path">Path to intermediate file saved on disk.</param>
344 /// <param name="tableDefinitions">Collection containing TableDefinitions to use when reconstituting the intermediate.</param>
345 /// <param name="suppressVersionCheck">Suppress checking for wix.dll version mismatches.</param>
346 /// <returns>Returns the loaded intermediate.</returns>
347 public static Intermediate Load(string path, TableDefinitionCollection tableDefinitions, bool suppressVersionCheck)
348 { 278 {
349 using (FileStream stream = File.OpenRead(path)) 279 var embeddedFields = this.Sections.SelectMany(s => s.Tuples)
350 using (FileStructure fs = FileStructure.Read(stream)) 280 .SelectMany(t => t.Fields)
281 .Where(f => f?.Type == IntermediateFieldType.Path)
282 .Select(f => f.AsPath())
283 .Where(f => f.Embed)
284 .ToList();
285
286 var savedEmbedFields = new Dictionary<string, IntermediateFieldPathValue>(StringComparer.OrdinalIgnoreCase);
287 var uniqueEntryNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
288
289 foreach (var embeddedField in embeddedFields)
351 { 290 {
352 if (FileFormat.Wixobj != fs.FileFormat) 291 var key = String.Concat(embeddedField.BaseUri?.AbsoluteUri, "?", embeddedField.Path);
292
293 if (savedEmbedFields.TryGetValue(key, out var existing))
353 { 294 {
354 throw new WixUnexpectedFileFormatException(path, FileFormat.Wixobj, fs.FileFormat); 295 embeddedField.Path = existing.Path;
355 } 296 }
356 297 else
357 Uri uri = new Uri(Path.GetFullPath(path));
358 using (XmlReader reader = XmlReader.Create(fs.GetDataStream(), null, uri.AbsoluteUri))
359 { 298 {
360 try 299 var entryName = CalculateUniqueEntryName(uniqueEntryNames, embeddedField.Path);
300
301 if (embeddedField.BaseUri == null)
361 { 302 {
362 reader.MoveToContent(); 303 wixout.ImportDataStream(entryName, embeddedField.Path);
363 return Intermediate.Read(reader, tableDefinitions, suppressVersionCheck);
364 } 304 }
365 catch (XmlException xe) 305 else // open the container specified in baseUri and copy the correct stream out of it.
366 { 306 {
367 throw new WixCorruptFileException(path, fs.FileFormat, xe); 307 using (var otherWixout = WixOutput.Read(embeddedField.BaseUri))
308 using (var stream = otherWixout.GetDataStream(embeddedField.Path))
309 using (var target = wixout.CreateDataStream(entryName))
310 {
311 stream.CopyTo(target);
312 }
368 } 313 }
369 }
370 }
371 }
372 314
373 /// <summary> 315 embeddedField.Path = entryName;
374 /// Saves an intermediate to a path on disk.
375 /// </summary>
376 /// <param name="path">Path to save intermediate file to disk.</param>
377 public void Save(string path)
378 {
379 Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(path)));
380 316
381 using (var stream = File.Create(path)) 317 savedEmbedFields.Add(key, embeddedField);
382 using (var fs = FileStructure.Create(stream, FileFormat.Wixobj, null)) 318 }
383 using (var writer = XmlWriter.Create(fs.GetDataStream()))
384 {
385 writer.WriteStartDocument();
386 this.Write(writer);
387 writer.WriteEndDocument();
388 } 319 }
389 } 320 }
390 321
391 /// <summary> 322 private void SaveIR(WixOutput wixout)
392 /// Parse an intermediate from an XML format.
393 /// </summary>
394 /// <param name="reader">XmlReader where the intermediate is persisted.</param>
395 /// <param name="tableDefinitions">TableDefinitions to use in the intermediate.</param>
396 /// <param name="suppressVersionCheck">Suppress checking for wix.dll version mismatch.</param>
397 /// <returns>The parsed Intermediate.</returns>
398 private static Intermediate Read(XmlReader reader, TableDefinitionCollection tableDefinitions, bool suppressVersionCheck)
399 { 323 {
400 if ("wixObject" != reader.LocalName) 324 using (var writer = new StreamWriter(wixout.CreateDataStream(WixOutputStreamName)))
401 { 325 {
402 throw new XmlException(); 326 var jsonObject = new JsonObject
403 } 327 {
404 328 { "id", this.Id },
405 bool empty = reader.IsEmptyElement; 329 { "version", Intermediate.CurrentVersion.ToString() }
406 Version objVersion = null; 330 };
407 string id = null;
408 331
409 while (reader.MoveToNextAttribute()) 332 var sectionsJson = new JsonArray(this.Sections.Count);
410 { 333 foreach (var section in this.Sections)
411 switch (reader.LocalName)
412 { 334 {
413 case "version": 335 var sectionJson = section.Serialize();
414 objVersion = new Version(reader.Value); 336 sectionsJson.Add(sectionJson);
415 break;
416 case "id":
417 id = reader.Value;
418 break;
419 } 337 }
420 }
421
422 if (!suppressVersionCheck && null != objVersion && !Intermediate.CurrentVersion.Equals(objVersion))
423 {
424 throw new WixException(WixDataErrors.VersionMismatch(SourceLineNumber.CreateFromUri(reader.BaseURI), "object", objVersion.ToString(), Intermediate.CurrentVersion.ToString()));
425 }
426 338
427 Intermediate intermediate = new Intermediate(); 339 jsonObject.Add("sections", sectionsJson);
428 intermediate.id = id;
429 340
430 if (!empty) 341 var customDefinitions = this.GetCustomDefinitionsInSections();
431 {
432 bool done = false;
433 342
434 while (!done && reader.Read()) 343 if (customDefinitions.Count > 0)
435 { 344 {
436 switch (reader.NodeType) 345 var customDefinitionsJson = new JsonArray(customDefinitions.Count);
346
347 foreach (var kvp in customDefinitions.OrderBy(d => d.Key))
437 { 348 {
438 case XmlNodeType.Element: 349 var customDefinitionJson = kvp.Value.Serialize();
439 switch (reader.LocalName) 350 customDefinitionsJson.Add(customDefinitionJson);
440 {
441 case "section":
442 intermediate.AddSection(Section.Read(reader, tableDefinitions));
443 break;
444 default:
445 throw new XmlException();
446 }
447 break;
448 case XmlNodeType.EndElement:
449 done = true;
450 break;
451 } 351 }
352
353 jsonObject.Add("definitions", customDefinitionsJson);
452 } 354 }
453 355
454 if (!done) 356 if (this.Localizations.Any())
455 { 357 {
456 throw new XmlException(); 358 var localizationsJson = new JsonArray();
359 foreach (var localization in this.Localizations)
360 {
361 var localizationJson = localization.Serialize();
362 localizationsJson.Add(localizationJson);
363 }
364
365 jsonObject.Add("localizations", localizationsJson);
457 } 366 }
458 }
459 367
460 return intermediate; 368 var json = SimpleJson.SerializeObject(jsonObject);
369 writer.Write(json);
370 }
461 } 371 }
462 372
463 /// <summary> 373 private static string CalculateUniqueEntryName(ISet<string> entryNames, string path)
464 /// Persists an intermediate in an XML format.
465 /// </summary>
466 /// <param name="writer">XmlWriter where the Intermediate should persist itself as XML.</param>
467 private void Write(XmlWriter writer)
468 { 374 {
469 writer.WriteStartElement("wixObject", XmlNamespaceUri); 375 var filename = Path.GetFileName(path);
470 376 var entryName = "wix-ir/" + filename;
471 writer.WriteAttributeString("version", Intermediate.CurrentVersion.ToString()); 377 var i = 0;
472
473 writer.WriteAttributeString("id", this.id);
474 378
475 foreach (Section section in this.Sections) 379 while (!entryNames.Add(entryName))
476 { 380 {
477 section.Write(writer); 381 entryName = $"wix-ir/{filename}-{++i}";
478 } 382 }
479 383
480 writer.WriteEndElement(); 384 return entryName;
481 } 385 }
482#endif
483 386
484 private Dictionary<string, IntermediateTupleDefinition> GetCustomDefinitionsInSections() 387 private Dictionary<string, IntermediateTupleDefinition> GetCustomDefinitionsInSections()
485 { 388 {