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