summaryrefslogtreecommitdiff
path: root/src/tools/heat/Extensibility/BaseMutatorExtension.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/heat/Extensibility/BaseMutatorExtension.cs')
-rw-r--r--src/tools/heat/Extensibility/BaseMutatorExtension.cs202
1 files changed, 202 insertions, 0 deletions
diff --git a/src/tools/heat/Extensibility/BaseMutatorExtension.cs b/src/tools/heat/Extensibility/BaseMutatorExtension.cs
new file mode 100644
index 00000000..c36a8ed1
--- /dev/null
+++ b/src/tools/heat/Extensibility/BaseMutatorExtension.cs
@@ -0,0 +1,202 @@
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.Harvesters.Extensibility
4{
5 using System;
6 using System.Collections.Generic;
7 using System.Text;
8 using Wix = WixToolset.Harvesters.Serialize;
9
10 /// <summary>
11 /// The base mutator extension. Any of these methods can be overridden to change
12 /// the behavior of the mutator.
13 /// </summary>
14 public abstract class BaseMutatorExtension : IMutatorExtension
15 {
16 /// <summary>
17 /// Gets or sets the mutator core for the extension.
18 /// </summary>
19 /// <value>The mutator core for the extension.</value>
20 public IHarvesterCore Core { get; set; }
21
22 /// <summary>
23 /// Gets the sequence of the extension.
24 /// </summary>
25 /// <value>The sequence of the extension.</value>
26 public abstract int Sequence { get; }
27
28 /// <summary>
29 /// Mutate a WiX document.
30 /// </summary>
31 /// <param name="wix">The Wix document element.</param>
32 public virtual void Mutate(Wix.Wix wix)
33 {
34 }
35
36 /// <summary>
37 /// Mutate a WiX document as a string.
38 /// </summary>
39 /// <param name="wixString">The Wix document element as a string.</param>
40 /// <returns>The mutated Wix document as a string.</returns>
41 public virtual string Mutate(string wixString)
42 {
43 return wixString;
44 }
45
46 /// <summary>
47 /// Generate unique MSI identifiers.
48 /// </summary>
49 protected class IdentifierGenerator
50 {
51 /// <summary>
52 ///
53 /// </summary>
54 public const int MaxProductIdentifierLength = 72;
55
56 /// <summary>
57 ///
58 /// </summary>
59 public const int MaxModuleIdentifierLength = 35;
60
61 private string baseName;
62 private int maxLength;
63 private Dictionary<string, object> existingIdentifiers;
64 private Dictionary<string, object> possibleIdentifiers;
65 private IHarvesterCore harvesterCore;
66
67 /// <summary>
68 /// Instantiate a new IdentifierGenerator.
69 /// </summary>
70 /// <param name="baseName">The base resource name to use if a resource name contains no usable characters.</param>
71 /// <param name="harvesterCore"></param>
72 public IdentifierGenerator(string baseName, IHarvesterCore harvesterCore)
73 {
74 this.baseName = baseName;
75 this.maxLength = IdentifierGenerator.MaxProductIdentifierLength;
76 this.existingIdentifiers = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
77 this.possibleIdentifiers = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
78 this.harvesterCore = harvesterCore;
79 }
80
81 /// <summary>
82 /// Gets or sets the maximum length for generated identifiers.
83 /// </summary>
84 /// <value>Maximum length for generated identifiers. (Default is 72.)</value>
85 public int MaxIdentifierLength
86 {
87 get { return this.maxLength; }
88 set { this.maxLength = value; }
89 }
90
91 /// <summary>
92 /// Index an existing identifier for collision detection.
93 /// </summary>
94 /// <param name="identifier">The identifier.</param>
95 public void IndexExistingIdentifier(string identifier)
96 {
97 if (null == identifier)
98 {
99 throw new ArgumentNullException("identifier");
100 }
101
102 this.existingIdentifiers[identifier] = null;
103 }
104
105 /// <summary>
106 /// Index a resource name for collision detection.
107 /// </summary>
108 /// <param name="name">The resource name.</param>
109 public void IndexName(string name)
110 {
111 if (null == name)
112 {
113 throw new ArgumentNullException("name");
114 }
115
116 string identifier = this.CreateIdentifier(name, 0);
117
118 if (this.possibleIdentifiers.ContainsKey(identifier))
119 {
120 this.possibleIdentifiers[identifier] = String.Empty;
121 }
122 else
123 {
124 this.possibleIdentifiers.Add(identifier, null);
125 }
126 }
127
128 /// <summary>
129 /// Get the identifier for the given resource name.
130 /// </summary>
131 /// <param name="name">The resource name.</param>
132 /// <returns>A legal MSI identifier.</returns>
133 public string GetIdentifier(string name)
134 {
135 if (null == name)
136 {
137 throw new ArgumentNullException("name");
138 }
139
140 for (int i = 0; i <= Int32.MaxValue; i++)
141 {
142 string identifier = this.CreateIdentifier(name, i);
143
144 if (this.existingIdentifiers.ContainsKey(identifier) || // already used
145 (0 == i && 0 != this.possibleIdentifiers.Count && null != this.possibleIdentifiers[identifier]) || // needs an index because its duplicated
146 (0 != i && this.possibleIdentifiers.ContainsKey(identifier))) // collides with another possible identifier
147 {
148 continue;
149 }
150 else // use this identifier
151 {
152 this.existingIdentifiers.Add(identifier, null);
153
154 return identifier;
155 }
156 }
157
158 throw new InvalidOperationException("Could not find a unique identifier for the given resource name.");
159 }
160
161 /// <summary>
162 /// Create a legal MSI identifier from a resource name and an index.
163 /// </summary>
164 /// <param name="name">The name of the resource for which an identifier should be created.</param>
165 /// <param name="index">An index to append to the end of the identifier to make it unique.</param>
166 /// <returns>A legal MSI identifier.</returns>
167 public string CreateIdentifier(string name, int index)
168 {
169 if (null == name)
170 {
171 throw new ArgumentNullException("name");
172 }
173
174 StringBuilder identifier = new StringBuilder();
175
176 // Convert the name to a standard MSI identifier
177 identifier.Append(this.harvesterCore.CreateIdentifierFromFilename(name));
178
179 // no legal identifier characters were found, use the base id instead
180 if (0 == identifier.Length)
181 {
182 identifier.Append(this.baseName);
183 }
184
185 // truncate the identifier if it's too long (reserve 3 characters for up to 99 collisions)
186 int adjustedMaxLength = this.MaxIdentifierLength - (index != 0 ? 3 : 0);
187 if (adjustedMaxLength < identifier.Length)
188 {
189 identifier.Length = adjustedMaxLength;
190 }
191
192 // if the index is not zero, then append it to the identifier name
193 if (0 != index)
194 {
195 identifier.AppendFormat("_{0}", index);
196 }
197
198 return identifier.ToString();
199 }
200 }
201 }
202}