diff options
Diffstat (limited to '')
-rw-r--r-- | src/WixToolset.Data/Library.cs | 297 |
1 files changed, 297 insertions, 0 deletions
diff --git a/src/WixToolset.Data/Library.cs b/src/WixToolset.Data/Library.cs new file mode 100644 index 00000000..bb04d216 --- /dev/null +++ b/src/WixToolset.Data/Library.cs | |||
@@ -0,0 +1,297 @@ | |||
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; | ||
6 | using System.Collections.Generic; | ||
7 | using System.IO; | ||
8 | using System.Linq; | ||
9 | using System.Xml; | ||
10 | |||
11 | /// <summary> | ||
12 | /// Object that represents a library file. | ||
13 | /// </summary> | ||
14 | public sealed class Library | ||
15 | { | ||
16 | public const string XmlNamespaceUri = "http://wixtoolset.org/schemas/v4/wixlib"; | ||
17 | private static readonly Version CurrentVersion = new Version("4.0.0.0"); | ||
18 | |||
19 | private string id; | ||
20 | private Dictionary<string, Localization> localizations; | ||
21 | private List<Section> sections; | ||
22 | |||
23 | /// <summary> | ||
24 | /// Instantiates a new empty library which is only useful from static creating methods. | ||
25 | /// </summary> | ||
26 | private Library() | ||
27 | { | ||
28 | this.localizations = new Dictionary<string, Localization>(); | ||
29 | this.sections = new List<Section>(); | ||
30 | } | ||
31 | |||
32 | /// <summary> | ||
33 | /// Instantiate a new library populated with sections. | ||
34 | /// </summary> | ||
35 | /// <param name="sections">Sections to add to the library.</param> | ||
36 | public Library(IEnumerable<Section> sections) | ||
37 | { | ||
38 | this.localizations = new Dictionary<string, Localization>(); | ||
39 | this.sections = new List<Section>(sections); | ||
40 | |||
41 | this.id = Convert.ToBase64String(Guid.NewGuid().ToByteArray()).TrimEnd('=').Replace('+', '.').Replace('/', '_'); | ||
42 | foreach (Section section in this.sections) | ||
43 | { | ||
44 | section.LibraryId = this.id; | ||
45 | } | ||
46 | } | ||
47 | |||
48 | /// <summary> | ||
49 | /// Get the sections contained in this library. | ||
50 | /// </summary> | ||
51 | /// <value>Sections contained in this library.</value> | ||
52 | public IEnumerable<Section> Sections { get { return this.sections; } } | ||
53 | |||
54 | /// <summary> | ||
55 | /// Add a localization file to this library. | ||
56 | /// </summary> | ||
57 | /// <param name="localization">The localization file to add.</param> | ||
58 | public void AddLocalization(Localization localization) | ||
59 | { | ||
60 | Localization existingCulture; | ||
61 | if (this.localizations.TryGetValue(localization.Culture, out existingCulture)) | ||
62 | { | ||
63 | existingCulture.Merge(localization); | ||
64 | } | ||
65 | else | ||
66 | { | ||
67 | this.localizations.Add(localization.Culture, localization); | ||
68 | } | ||
69 | } | ||
70 | |||
71 | /// <summary> | ||
72 | /// Gets localization files from this library that match the cultures passed in, in the order of the array of cultures. | ||
73 | /// </summary> | ||
74 | /// <param name="cultures">The list of cultures to get localizations for.</param> | ||
75 | /// <returns>All localizations contained in this library that match the set of cultures provided, in the same order.</returns> | ||
76 | public IEnumerable<Localization> GetLocalizations(string[] cultures) | ||
77 | { | ||
78 | foreach (string culture in cultures ?? new string[0]) | ||
79 | { | ||
80 | Localization localization; | ||
81 | if (this.localizations.TryGetValue(culture, out localization)) | ||
82 | { | ||
83 | yield return localization; | ||
84 | } | ||
85 | } | ||
86 | } | ||
87 | |||
88 | /// <summary> | ||
89 | /// Loads a library from a path on disk. | ||
90 | /// </summary> | ||
91 | /// <param name="path">Path to library file saved on disk.</param> | ||
92 | /// <param name="tableDefinitions">Collection containing TableDefinitions to use when reconstituting the intermediates.</param> | ||
93 | /// <param name="suppressVersionCheck">Suppresses wix.dll version mismatch check.</param> | ||
94 | /// <returns>Returns the loaded library.</returns> | ||
95 | public static Library Load(string path, TableDefinitionCollection tableDefinitions, bool suppressVersionCheck) | ||
96 | { | ||
97 | using (FileStream stream = File.OpenRead(path)) | ||
98 | { | ||
99 | return Load(stream, new Uri(Path.GetFullPath(path)), tableDefinitions, suppressVersionCheck); | ||
100 | } | ||
101 | } | ||
102 | |||
103 | /// <summary> | ||
104 | /// Loads a library from a stream. | ||
105 | /// </summary> | ||
106 | /// <param name="stream">Stream containing the library file.</param> | ||
107 | /// <param name="uri">Uri for finding this stream.</param> | ||
108 | /// <param name="tableDefinitions">Collection containing TableDefinitions to use when reconstituting the intermediates.</param> | ||
109 | /// <param name="suppressVersionCheck">Suppresses wix.dll version mismatch check.</param> | ||
110 | /// <returns>Returns the loaded library.</returns> | ||
111 | public static Library Load(Stream stream, Uri uri, TableDefinitionCollection tableDefinitions, bool suppressVersionCheck) | ||
112 | { | ||
113 | using (FileStructure fs = FileStructure.Read(stream)) | ||
114 | { | ||
115 | if (FileFormat.Wixlib != fs.FileFormat) | ||
116 | { | ||
117 | throw new WixUnexpectedFileFormatException(uri.LocalPath, FileFormat.Wixlib, fs.FileFormat); | ||
118 | } | ||
119 | |||
120 | using (XmlReader reader = XmlReader.Create(fs.GetDataStream(), null, uri.AbsoluteUri)) | ||
121 | { | ||
122 | try | ||
123 | { | ||
124 | reader.MoveToContent(); | ||
125 | return Library.Read(reader, tableDefinitions, suppressVersionCheck); | ||
126 | } | ||
127 | catch (XmlException xe) | ||
128 | { | ||
129 | throw new WixCorruptFileException(uri.LocalPath, fs.FileFormat, xe); | ||
130 | } | ||
131 | } | ||
132 | } | ||
133 | } | ||
134 | |||
135 | /// <summary> | ||
136 | /// Saves a library to a path on disk. | ||
137 | /// </summary> | ||
138 | /// <param name="path">Path to save library file to on disk.</param> | ||
139 | /// <param name="resolver">The WiX path resolver.</param> | ||
140 | public void Save(string path, ILibraryBinaryFileResolver resolver) | ||
141 | { | ||
142 | List<string> embedFilePaths = new List<string>(); | ||
143 | |||
144 | // Resolve paths to files that are to be embedded in the library. | ||
145 | if (null != resolver) | ||
146 | { | ||
147 | foreach (Table table in this.sections.SelectMany(s => s.Tables)) | ||
148 | { | ||
149 | foreach (Row row in table.Rows) | ||
150 | { | ||
151 | foreach (ObjectField objectField in row.Fields.Where(f => f is ObjectField)) | ||
152 | { | ||
153 | if (null != objectField.Data) | ||
154 | { | ||
155 | string file = resolver.Resolve(row.SourceLineNumbers, table.Name, (string)objectField.Data); | ||
156 | if (!String.IsNullOrEmpty(file)) | ||
157 | { | ||
158 | // File was successfully resolved so track the embedded index as the embedded file index. | ||
159 | objectField.EmbeddedFileIndex = embedFilePaths.Count; | ||
160 | embedFilePaths.Add(file); | ||
161 | } | ||
162 | else | ||
163 | { | ||
164 | Messaging.Instance.OnMessage(WixDataErrors.FileNotFound(row.SourceLineNumbers, (string)objectField.Data, table.Name)); | ||
165 | } | ||
166 | } | ||
167 | else // clear out embedded file id in case there was one there before. | ||
168 | { | ||
169 | objectField.EmbeddedFileIndex = null; | ||
170 | } | ||
171 | } | ||
172 | } | ||
173 | } | ||
174 | } | ||
175 | |||
176 | // Do not save the library if errors were found while resolving object paths. | ||
177 | if (Messaging.Instance.EncounteredError) | ||
178 | { | ||
179 | return; | ||
180 | } | ||
181 | |||
182 | // Ensure the location to output the library exists and write it out. | ||
183 | Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(path))); | ||
184 | |||
185 | using (FileStream stream = File.Create(path)) | ||
186 | using (FileStructure fs = FileStructure.Create(stream, FileFormat.Wixlib, embedFilePaths)) | ||
187 | using (XmlWriter writer = XmlWriter.Create(fs.GetDataStream())) | ||
188 | { | ||
189 | writer.WriteStartDocument(); | ||
190 | |||
191 | this.Write(writer); | ||
192 | |||
193 | writer.WriteEndDocument(); | ||
194 | } | ||
195 | } | ||
196 | |||
197 | /// <summary> | ||
198 | /// Parse the root library element. | ||
199 | /// </summary> | ||
200 | /// <param name="reader">XmlReader with library persisted as Xml.</param> | ||
201 | /// <param name="tableDefinitions">Collection containing TableDefinitions to use when reconstituting the intermediates.</param> | ||
202 | /// <param name="suppressVersionCheck">Suppresses check for wix.dll version mismatch.</param> | ||
203 | /// <returns>The parsed Library.</returns> | ||
204 | private static Library Read(XmlReader reader, TableDefinitionCollection tableDefinitions, bool suppressVersionCheck) | ||
205 | { | ||
206 | if (!reader.LocalName.Equals("wixLibrary")) | ||
207 | { | ||
208 | throw new XmlException(); | ||
209 | } | ||
210 | |||
211 | bool empty = reader.IsEmptyElement; | ||
212 | Library library = new Library(); | ||
213 | Version version = null; | ||
214 | |||
215 | while (reader.MoveToNextAttribute()) | ||
216 | { | ||
217 | switch (reader.LocalName) | ||
218 | { | ||
219 | case "version": | ||
220 | version = new Version(reader.Value); | ||
221 | break; | ||
222 | case "id": | ||
223 | library.id = reader.Value; | ||
224 | break; | ||
225 | } | ||
226 | } | ||
227 | |||
228 | if (!suppressVersionCheck && null != version && !Library.CurrentVersion.Equals(version)) | ||
229 | { | ||
230 | throw new WixException(WixDataErrors.VersionMismatch(SourceLineNumber.CreateFromUri(reader.BaseURI), "library", version.ToString(), Library.CurrentVersion.ToString())); | ||
231 | } | ||
232 | |||
233 | if (!empty) | ||
234 | { | ||
235 | bool done = false; | ||
236 | |||
237 | while (!done && (XmlNodeType.Element == reader.NodeType || reader.Read())) | ||
238 | { | ||
239 | switch (reader.NodeType) | ||
240 | { | ||
241 | case XmlNodeType.Element: | ||
242 | switch (reader.LocalName) | ||
243 | { | ||
244 | case "localization": | ||
245 | Localization localization = Localization.Read(reader, tableDefinitions); | ||
246 | library.localizations.Add(localization.Culture, localization); | ||
247 | break; | ||
248 | case "section": | ||
249 | Section section = Section.Read(reader, tableDefinitions); | ||
250 | section.LibraryId = library.id; | ||
251 | library.sections.Add(section); | ||
252 | break; | ||
253 | default: | ||
254 | throw new XmlException(); | ||
255 | } | ||
256 | break; | ||
257 | case XmlNodeType.EndElement: | ||
258 | done = true; | ||
259 | break; | ||
260 | } | ||
261 | } | ||
262 | |||
263 | if (!done) | ||
264 | { | ||
265 | throw new XmlException(); | ||
266 | } | ||
267 | } | ||
268 | |||
269 | return library; | ||
270 | } | ||
271 | |||
272 | /// <summary> | ||
273 | /// Persists a library in an XML format. | ||
274 | /// </summary> | ||
275 | /// <param name="writer">XmlWriter where the library should persist itself as XML.</param> | ||
276 | private void Write(XmlWriter writer) | ||
277 | { | ||
278 | writer.WriteStartElement("wixLibrary", XmlNamespaceUri); | ||
279 | |||
280 | writer.WriteAttributeString("version", CurrentVersion.ToString()); | ||
281 | |||
282 | writer.WriteAttributeString("id", this.id); | ||
283 | |||
284 | foreach (Localization localization in this.localizations.Values) | ||
285 | { | ||
286 | localization.Write(writer); | ||
287 | } | ||
288 | |||
289 | foreach (Section section in this.sections) | ||
290 | { | ||
291 | section.Write(writer); | ||
292 | } | ||
293 | |||
294 | writer.WriteEndElement(); | ||
295 | } | ||
296 | } | ||
297 | } | ||