| 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.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 | } |