aboutsummaryrefslogtreecommitdiff
path: root/src/ext/Dependency/wixext/DependencyDecompiler.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/ext/Dependency/wixext/DependencyDecompiler.cs')
-rw-r--r--src/ext/Dependency/wixext/DependencyDecompiler.cs347
1 files changed, 347 insertions, 0 deletions
diff --git a/src/ext/Dependency/wixext/DependencyDecompiler.cs b/src/ext/Dependency/wixext/DependencyDecompiler.cs
new file mode 100644
index 00000000..31de3097
--- /dev/null
+++ b/src/ext/Dependency/wixext/DependencyDecompiler.cs
@@ -0,0 +1,347 @@
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.Dependency
4{
5#if TODO_CONSIDER_DECOMPILER
6 using System;
7 using System.Collections.Generic;
8 using System.Collections.ObjectModel;
9 using WixToolset;
10 using WixToolset.Data;
11 using WixToolset.Extensibility;
12 using WixToolset.Extensions.Serialize.Dependency;
13 using Dependency = WixToolset.Extensions.Serialize.Dependency;
14 using Wix = WixToolset.Data.Serialize;
15
16 /// <summary>
17 /// The decompiler for the WiX toolset dependency extension.
18 /// </summary>
19 public sealed class DependencyDecompiler : DecompilerExtension
20 {
21 private RegistryKeyValueCollection registryValues;
22 private Dictionary<string, string> keyCache;
23
24 /// <summary>
25 /// Creates a new instance of the <see cref="DependencyDecompiler"/> class.
26 /// </summary>
27 public DependencyDecompiler()
28 {
29 this.registryValues = new RegistryKeyValueCollection();
30 this.keyCache = new Dictionary<string, string>();
31
32 this.TableDefinitions = DependencyExtensionData.GetExtensionTableDefinitions();
33 }
34
35 /// <summary>
36 /// Get the extensions library to be removed.
37 /// </summary>
38 /// <param name="tableDefinitions">Table definitions for library.</param>
39 /// <returns>Library to remove from decompiled output.</returns>
40 public override Library GetLibraryToRemove(TableDefinitionCollection tableDefinitions)
41 {
42 return DependencyExtensionData.GetExtensionLibrary(tableDefinitions);
43 }
44
45 /// <summary>
46 /// Decompiles an extension table.
47 /// </summary>
48 /// <param name="table">The table to decompile.</param>
49 public override void DecompileTable(Table table)
50 {
51 switch (table.Name)
52 {
53 case "WixDependencyProvider":
54 this.DecompileWixDependencyProviderTable(table);
55 break;
56
57 case "WixDependency":
58 this.DecompileWixDependencyTable(table);
59 break;
60
61 case "WixDependencyRef":
62 this.DecompileWixDependencyRefTable(table);
63 break;
64
65 default:
66 base.DecompileTable(table);
67 break;
68 }
69 }
70
71 /// <summary>
72 /// Finalize decompilation by removing registry values that the compiler writes.
73 /// </summary>
74 /// <param name="tables">The collection of all tables.</param>
75 public override void Finish(TableIndexedCollection tables)
76 {
77 // Remove generated registry rows.
78 this.FinalizeRegistryTable(tables);
79
80 // Remove extension properties.
81 this.FinalizeProperties();
82 }
83
84 /// <summary>
85 /// Decompiles the WixDependencyProvider table.
86 /// </summary>
87 /// <param name="table">The table to decompile.</param>
88 private void DecompileWixDependencyProviderTable(Table table)
89 {
90 foreach (Row row in table.Rows)
91 {
92 Provides provides = new Provides();
93
94 provides.Id = (string)row[0];
95 provides.Key = (string)row[2];
96
97 if (null != row[3])
98 {
99 provides.Version = (string)row[3];
100 }
101
102 if (null != row[4])
103 {
104 provides.DisplayName = (string)row[4];
105 }
106
107 // Nothing to parse for attributes currently.
108
109 Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", (string)row[1]);
110 if (null != component)
111 {
112 component.AddChild(provides);
113 }
114 else
115 {
116 this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", (string)row[1], "Component"));
117 }
118
119 // Index the provider to parent the RequiresRef elements.
120 this.Core.IndexElement(row, provides);
121
122 // Add the provider-specific registry keys to be removed during finalization.
123 // Only remove specific keys that the compiler writes.
124 string keyProvides = String.Concat(DependencyCommon.RegistryRoot, provides.Key);
125
126 this.registryValues.Add(keyProvides, null);
127 this.registryValues.Add(keyProvides, "Version");
128 this.registryValues.Add(keyProvides, "DisplayName");
129 this.registryValues.Add(keyProvides, "Attributes");
130
131 // Cache the provider key.
132 this.keyCache[provides.Id] = provides.Key;
133 }
134 }
135
136 /// <summary>
137 /// Decompiles the WixDependency table.
138 /// </summary>
139 /// <param name="table">The table to decompile.</param>
140 private void DecompileWixDependencyTable(Table table)
141 {
142 foreach (Row row in table.Rows)
143 {
144 Requires requires = new Requires();
145
146 requires.Id = (string)row[0];
147 requires.ProviderKey = (string)row[1];
148
149 if (null != row[2])
150 {
151 requires.Minimum = (string)row[2];
152 }
153
154 if (null != row[3])
155 {
156 requires.Maximum = (string)row[3];
157 }
158
159 if (null != row[4])
160 {
161 int attributes = (int)row[4];
162
163 if (0 != (attributes & DependencyCommon.RequiresAttributesMinVersionInclusive))
164 {
165 requires.IncludeMinimum = Dependency.YesNoType.yes;
166 }
167
168 if (0 != (attributes & DependencyCommon.RequiresAttributesMaxVersionInclusive))
169 {
170 requires.IncludeMaximum = Dependency.YesNoType.yes;
171 }
172 }
173
174 this.Core.RootElement.AddChild(requires);
175
176 // Cache the requires key.
177 this.keyCache[requires.Id] = requires.ProviderKey;
178 }
179 }
180
181 /// <summary>
182 /// Decompiles the WixDependencyRef table.
183 /// </summary>
184 /// <param name="table">The table to decompile.</param>
185 private void DecompileWixDependencyRefTable(Table table)
186 {
187 foreach (Row row in table.Rows)
188 {
189 RequiresRef requiresRef = new RequiresRef();
190
191 requiresRef.Id = (string)row[1];
192
193 Provides provides = (Provides)this.Core.GetIndexedElement("WixDependencyProvider", (string)row[0]);
194 if (null != provides)
195 {
196 provides.AddChild(requiresRef);
197 }
198 else
199 {
200 this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "WixDependencyProvider_", (string)row[0], "WixDependencyProvider"));
201 }
202
203 // Get the cached keys for the provider and dependency IDs and generate registry rows.
204 string providesKey = null;
205 string requiresKey = null;
206
207 if (null != provides && this.keyCache.ContainsKey(provides.Id))
208 {
209 providesKey = this.keyCache[provides.Id];
210 }
211 else
212 {
213 this.Core.OnMessage(DependencyWarnings.ProvidesKeyNotFound(row.SourceLineNumbers, provides.Id));
214 }
215
216 if (this.keyCache.ContainsKey(requiresRef.Id))
217 {
218 requiresKey = this.keyCache[requiresRef.Id];
219 }
220 else
221 {
222 this.Core.OnMessage(DependencyWarnings.RequiresKeyNotFound(row.SourceLineNumbers, requiresRef.Id));
223 }
224
225 if (!this.Core.EncounteredError)
226 {
227 // Add the dependency-specific registry keys to be removed during finalization.
228 // Only remove specific keys that the compiler writes.
229 string keyRequires = String.Format(@"{0}{1}\{2}\{3}", DependencyCommon.RegistryRoot, requiresKey, DependencyCommon.RegistryDependents, providesKey);
230
231 this.registryValues.Add(keyRequires, "*");
232 this.registryValues.Add(keyRequires, "MinVersion");
233 this.registryValues.Add(keyRequires, "MaxVersion");
234 this.registryValues.Add(keyRequires, "Attributes");
235 }
236 }
237 }
238
239 /// <summary>
240 /// Removes rows from the Registry table that are generated by this extension.
241 /// </summary>
242 /// <param name="tables">The collection of tables.</param>
243 private void FinalizeRegistryTable(TableIndexedCollection tables)
244 {
245 Table registryTable = tables["Registry"];
246 if (null != registryTable)
247 {
248 foreach (Row registryRow in registryTable.Rows)
249 {
250 // Check if the compiler writes this registry value; if so, it should be removed.
251 if (this.registryValues.Contains(registryRow))
252 {
253 Wix.ISchemaElement elem = this.Core.GetIndexedElement(registryRow);
254
255 // If the registry row was found, remove it from its parent.
256 if (null != elem && null != elem.ParentElement)
257 {
258 Wix.IParentElement elemParent = elem.ParentElement as Wix.IParentElement;
259 if (null != elemParent)
260 {
261 elemParent.RemoveChild(elem);
262 }
263 }
264 }
265 }
266 }
267 }
268
269 /// <summary>
270 /// Removes properties defined by this extension.
271 /// </summary>
272 /// <param name="tables">The collection of tables.</param>
273 private void FinalizeProperties()
274 {
275 string[] properties = new string[] { "DISABLEDEPENDENCYCHECK", "IGNOREDEPENDENCIES" };
276 foreach (string property in properties)
277 {
278 Wix.Property elem = this.Core.GetIndexedElement("Property", property) as Wix.Property;
279 if (null != elem)
280 {
281 // If a value is defined, log a warning we're removing it.
282 if (!String.IsNullOrEmpty(elem.Value))
283 {
284 this.Core.OnMessage(DependencyWarnings.PropertyRemoved(elem.Id));
285 }
286
287 // If the property row was found, remove it from its parent.
288 if (null != elem.ParentElement)
289 {
290 Wix.IParentElement elemParent = elem.ParentElement as Wix.IParentElement;
291 if (null != elemParent)
292 {
293 elemParent.RemoveChild(elem);
294 }
295 }
296 }
297 }
298 }
299
300 /// <summary>
301 /// Provides an O(1) lookup for registry key and value name pairs for use in the decompiler.
302 /// </summary>
303 private sealed class RegistryKeyValueCollection : KeyedCollection<int, KeyValuePair<string, string>>
304 {
305 /// <summary>
306 /// Adds the registry key and value name pair to the collection if it doesn't already exist.
307 /// </summary>
308 /// <param name="key">The registry key to add.</param>
309 /// <param name="name">The registry value name to add.</param>
310 internal void Add(string key, string name)
311 {
312 KeyValuePair<string, string> pair = new KeyValuePair<string, string>(key, name);
313 if (!this.Contains(pair))
314 {
315 this.Add(pair);
316 }
317 }
318
319 /// <summary>
320 /// Returns whether the collection contains the registry key and value name pair from the <see cref="Row"/>.
321 /// </summary>
322 /// <param name="row">The registry <see cref="Row"/> to search for.</param>
323 /// <returns>True if the collection contains the registry key and value name pair from the <see cref="Row"/>; otherwise, false.</returns>
324 internal bool Contains(Row row)
325 {
326 if (null == row)
327 {
328 return false;
329 }
330
331 KeyValuePair<string, string> pair = new KeyValuePair<string, string>((string)row[2], (string)row[3]);
332 return this.Contains(pair);
333 }
334
335 /// <summary>
336 /// Return the hash code of the key and value pair concatenated with a colon as a delimiter.
337 /// </summary>
338 /// <param name="pair">The registry key and value name pair.</param>
339 /// <returns></returns>
340 protected override int GetKeyForItem(KeyValuePair<string, string> pair)
341 {
342 return String.Concat(pair.Key, ":", pair.Value).GetHashCode();
343 }
344 }
345 }
346#endif
347}