diff options
Diffstat (limited to 'src/WixToolset.Core.WindowsInstaller/PatchTransform.cs')
-rw-r--r-- | src/WixToolset.Core.WindowsInstaller/PatchTransform.cs | 277 |
1 files changed, 277 insertions, 0 deletions
diff --git a/src/WixToolset.Core.WindowsInstaller/PatchTransform.cs b/src/WixToolset.Core.WindowsInstaller/PatchTransform.cs new file mode 100644 index 00000000..9ba14843 --- /dev/null +++ b/src/WixToolset.Core.WindowsInstaller/PatchTransform.cs | |||
@@ -0,0 +1,277 @@ | |||
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 | ||
4 | { | ||
5 | using System; | ||
6 | using System.Collections; | ||
7 | using System.Globalization; | ||
8 | using System.Text; | ||
9 | using System.Text.RegularExpressions; | ||
10 | using WixToolset.Data; | ||
11 | using WixToolset.Extensibility; | ||
12 | |||
13 | public class PatchTransform : IMessageHandler | ||
14 | { | ||
15 | private string baseline; | ||
16 | private Intermediate transform; | ||
17 | private string transformPath; | ||
18 | |||
19 | public string Baseline | ||
20 | { | ||
21 | get { return this.baseline; } | ||
22 | } | ||
23 | |||
24 | public Intermediate Transform | ||
25 | { | ||
26 | get | ||
27 | { | ||
28 | if (null == this.transform) | ||
29 | { | ||
30 | this.transform = Intermediate.Load(this.transformPath, false); | ||
31 | } | ||
32 | |||
33 | return this.transform; | ||
34 | } | ||
35 | } | ||
36 | |||
37 | public string TransformPath | ||
38 | { | ||
39 | get { return this.transformPath; } | ||
40 | } | ||
41 | |||
42 | public PatchTransform(string transformPath, string baseline) | ||
43 | { | ||
44 | this.transformPath = transformPath; | ||
45 | this.baseline = baseline; | ||
46 | } | ||
47 | |||
48 | /// <summary> | ||
49 | /// Validates that the differences in the transform are valid for patch transforms. | ||
50 | /// </summary> | ||
51 | public void Validate() | ||
52 | { | ||
53 | #if REVISIT_FOR_PATCHING | ||
54 | // Changing the ProdocutCode in a patch transform is not recommended. | ||
55 | Table propertyTable = this.Transform.Tables["Property"]; | ||
56 | if (null != propertyTable) | ||
57 | { | ||
58 | foreach (Row row in propertyTable.Rows) | ||
59 | { | ||
60 | // Only interested in modified rows; fast check. | ||
61 | if (RowOperation.Modify == row.Operation) | ||
62 | { | ||
63 | if (0 == String.CompareOrdinal("ProductCode", (string)row[0])) | ||
64 | { | ||
65 | this.OnMessage(WixWarnings.MajorUpgradePatchNotRecommended()); | ||
66 | } | ||
67 | } | ||
68 | } | ||
69 | } | ||
70 | |||
71 | // If there is nothing in the component table we can return early because the remaining checks are component based. | ||
72 | Table componentTable = this.Transform.Tables["Component"]; | ||
73 | if (null == componentTable) | ||
74 | { | ||
75 | return; | ||
76 | } | ||
77 | |||
78 | // Index Feature table row operations | ||
79 | Table featureTable = this.Transform.Tables["Feature"]; | ||
80 | Table featureComponentsTable = this.Transform.Tables["FeatureComponents"]; | ||
81 | Hashtable featureOps = null; | ||
82 | if (null != featureTable) | ||
83 | { | ||
84 | int capacity = featureTable.Rows.Count; | ||
85 | featureOps = new Hashtable(capacity); | ||
86 | |||
87 | foreach (Row row in featureTable.Rows) | ||
88 | { | ||
89 | featureOps[(string)row[0]] = row.Operation; | ||
90 | } | ||
91 | } | ||
92 | else | ||
93 | { | ||
94 | featureOps = new Hashtable(); | ||
95 | } | ||
96 | |||
97 | // Index Component table and check for keypath modifications | ||
98 | Hashtable deletedComponent = new Hashtable(); | ||
99 | Hashtable componentKeyPath = new Hashtable(); | ||
100 | foreach (Row row in componentTable.Rows) | ||
101 | { | ||
102 | string id = row.Fields[0].Data.ToString(); | ||
103 | string keypath = (null == row.Fields[5].Data) ? String.Empty : row.Fields[5].Data.ToString(); | ||
104 | |||
105 | componentKeyPath.Add(id, keypath); | ||
106 | if (RowOperation.Delete == row.Operation) | ||
107 | { | ||
108 | deletedComponent.Add(id, row); | ||
109 | } | ||
110 | else if (RowOperation.Modify == row.Operation) | ||
111 | { | ||
112 | if (row.Fields[1].Modified) | ||
113 | { | ||
114 | // Changing the guid of a component is equal to deleting the old one and adding a new one. | ||
115 | deletedComponent.Add(id, row); | ||
116 | } | ||
117 | |||
118 | // If the keypath is modified its an error | ||
119 | if (row.Fields[5].Modified) | ||
120 | { | ||
121 | this.OnMessage(WixErrors.InvalidKeypathChange(row.SourceLineNumbers, id, this.transformPath)); | ||
122 | } | ||
123 | } | ||
124 | } | ||
125 | |||
126 | // Verify changes in the file table | ||
127 | Table fileTable = this.Transform.Tables["File"]; | ||
128 | if (null != fileTable) | ||
129 | { | ||
130 | Hashtable componentWithChangedKeyPath = new Hashtable(); | ||
131 | foreach (Row row in fileTable.Rows) | ||
132 | { | ||
133 | if (RowOperation.None != row.Operation) | ||
134 | { | ||
135 | string fileId = row.Fields[0].Data.ToString(); | ||
136 | string componentId = row.Fields[1].Data.ToString(); | ||
137 | |||
138 | // If this file is the keypath of a component | ||
139 | if (String.Equals((string)componentKeyPath[componentId], fileId, StringComparison.Ordinal)) | ||
140 | { | ||
141 | if (row.Fields[2].Modified) | ||
142 | { | ||
143 | // You cant change the filename of a file that is the keypath of a component. | ||
144 | this.OnMessage(WixErrors.InvalidKeypathChange(row.SourceLineNumbers, componentId, this.transformPath)); | ||
145 | } | ||
146 | |||
147 | if (!componentWithChangedKeyPath.ContainsKey(componentId)) | ||
148 | { | ||
149 | componentWithChangedKeyPath.Add(componentId, fileId); | ||
150 | } | ||
151 | } | ||
152 | |||
153 | if (RowOperation.Delete == row.Operation) | ||
154 | { | ||
155 | // If the file is removed from a component that is not deleted. | ||
156 | if (!deletedComponent.ContainsKey(componentId)) | ||
157 | { | ||
158 | bool foundRemoveFileEntry = false; | ||
159 | string filename = Common.GetName((string)row[2], false, true); | ||
160 | |||
161 | Table removeFileTable = this.Transform.Tables["RemoveFile"]; | ||
162 | if (null != removeFileTable) | ||
163 | { | ||
164 | foreach (Row removeFileRow in removeFileTable.Rows) | ||
165 | { | ||
166 | if (RowOperation.Delete == removeFileRow.Operation) | ||
167 | { | ||
168 | continue; | ||
169 | } | ||
170 | |||
171 | if (componentId == (string)removeFileRow[1]) | ||
172 | { | ||
173 | // Check if there is a RemoveFile entry for this file | ||
174 | if (null != removeFileRow[2]) | ||
175 | { | ||
176 | string removeFileName = Common.GetName((string)removeFileRow[2], false, true); | ||
177 | |||
178 | // Convert the MSI format for a wildcard string to Regex format. | ||
179 | removeFileName = removeFileName.Replace('.', '|').Replace('?', '.').Replace("*", ".*").Replace("|", "\\."); | ||
180 | |||
181 | Regex regex = new Regex(removeFileName, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); | ||
182 | if (regex.IsMatch(filename)) | ||
183 | { | ||
184 | foundRemoveFileEntry = true; | ||
185 | break; | ||
186 | } | ||
187 | } | ||
188 | } | ||
189 | } | ||
190 | } | ||
191 | |||
192 | if (!foundRemoveFileEntry) | ||
193 | { | ||
194 | this.OnMessage(WixWarnings.InvalidRemoveFile(row.SourceLineNumbers, fileId, componentId)); | ||
195 | } | ||
196 | } | ||
197 | } | ||
198 | } | ||
199 | } | ||
200 | } | ||
201 | |||
202 | if (0 < deletedComponent.Count) | ||
203 | { | ||
204 | // Index FeatureComponents table. | ||
205 | Hashtable featureComponents = new Hashtable(); | ||
206 | |||
207 | if (null != featureComponentsTable) | ||
208 | { | ||
209 | foreach (Row row in featureComponentsTable.Rows) | ||
210 | { | ||
211 | ArrayList features; | ||
212 | string componentId = row.Fields[1].Data.ToString(); | ||
213 | |||
214 | if (featureComponents.Contains(componentId)) | ||
215 | { | ||
216 | features = (ArrayList)featureComponents[componentId]; | ||
217 | } | ||
218 | else | ||
219 | { | ||
220 | features = new ArrayList(); | ||
221 | featureComponents.Add(componentId, features); | ||
222 | } | ||
223 | features.Add(row.Fields[0].Data.ToString()); | ||
224 | } | ||
225 | } | ||
226 | |||
227 | // Check to make sure if a component was deleted, the feature was too. | ||
228 | foreach (DictionaryEntry entry in deletedComponent) | ||
229 | { | ||
230 | if (featureComponents.Contains(entry.Key.ToString())) | ||
231 | { | ||
232 | ArrayList features = (ArrayList)featureComponents[entry.Key.ToString()]; | ||
233 | foreach (string featureId in features) | ||
234 | { | ||
235 | if (!featureOps.ContainsKey(featureId) || RowOperation.Delete != (RowOperation)featureOps[featureId]) | ||
236 | { | ||
237 | // The feature was not deleted. | ||
238 | this.OnMessage(WixErrors.InvalidRemoveComponent(((Row)entry.Value).SourceLineNumbers, entry.Key.ToString(), featureId, this.transformPath)); | ||
239 | } | ||
240 | } | ||
241 | } | ||
242 | } | ||
243 | } | ||
244 | |||
245 | // Warn if new components are added to existing features | ||
246 | if (null != featureComponentsTable) | ||
247 | { | ||
248 | foreach (Row row in featureComponentsTable.Rows) | ||
249 | { | ||
250 | if (RowOperation.Add == row.Operation) | ||
251 | { | ||
252 | // Check if the feature is in the Feature table | ||
253 | string feature_ = (string)row[0]; | ||
254 | string component_ = (string)row[1]; | ||
255 | |||
256 | // Features may not be present if not referenced | ||
257 | if (!featureOps.ContainsKey(feature_) || RowOperation.Add != (RowOperation)featureOps[feature_]) | ||
258 | { | ||
259 | this.OnMessage(WixWarnings.NewComponentAddedToExistingFeature(row.SourceLineNumbers, component_, feature_, this.transformPath)); | ||
260 | } | ||
261 | } | ||
262 | } | ||
263 | } | ||
264 | #endif | ||
265 | throw new NotImplementedException(); | ||
266 | } | ||
267 | |||
268 | /// <summary> | ||
269 | /// Sends a message to the message delegate if there is one. | ||
270 | /// </summary> | ||
271 | /// <param name="mea">Message event arguments.</param> | ||
272 | public void OnMessage(MessageEventArgs e) | ||
273 | { | ||
274 | Messaging.Instance.OnMessage(e); | ||
275 | } | ||
276 | } | ||
277 | } | ||