aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Core.WindowsInstaller/ValidatorExtension.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/WixToolset.Core.WindowsInstaller/ValidatorExtension.cs')
-rw-r--r--src/WixToolset.Core.WindowsInstaller/ValidatorExtension.cs299
1 files changed, 299 insertions, 0 deletions
diff --git a/src/WixToolset.Core.WindowsInstaller/ValidatorExtension.cs b/src/WixToolset.Core.WindowsInstaller/ValidatorExtension.cs
new file mode 100644
index 00000000..44ec3106
--- /dev/null
+++ b/src/WixToolset.Core.WindowsInstaller/ValidatorExtension.cs
@@ -0,0 +1,299 @@
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;
7 using WixToolset.Data;
8
9 /// <summary>
10 /// Base class for creating a validator extension. This default implementation
11 /// will fire and event with the ICE name and description.
12 /// </summary>
13 public class ValidatorExtension : IMessageHandler
14 {
15 private string databaseFile;
16 private Hashtable indexedSourceLineNumbers;
17 private Output output;
18 private SourceLineNumber sourceLineNumbers;
19
20 /// <summary>
21 /// Instantiate a new <see cref="ValidatorExtension"/>.
22 /// </summary>
23 public ValidatorExtension()
24 {
25 }
26
27 /// <summary>
28 /// Gets or sets the path to the database to validate.
29 /// </summary>
30 /// <value>The path to the database to validate.</value>
31 public string DatabaseFile
32 {
33 get { return this.databaseFile; }
34 set { this.databaseFile = value; }
35 }
36
37 /// <summary>
38 /// Gets or sets the <see cref="Output"/> for finding source line information.
39 /// </summary>
40 /// <value>The <see cref="Output"/> for finding source line information.</value>
41 public Output Output
42 {
43 get { return this.output; }
44 set { this.output = value; }
45 }
46
47 /// <summary>
48 /// Called at the beginning of the validation of a database file.
49 /// </summary>
50 /// <remarks>
51 /// <para>The <see cref="Validator"/> will set
52 /// <see cref="DatabaseFile"/> before calling InitializeValidator.</para>
53 /// <para><b>Notes to Inheritors:</b> When overriding
54 /// <b>InitializeValidator</b> in a derived class, be sure to call
55 /// the base class's <b>InitializeValidator</b> to thoroughly
56 /// initialize the extension.</para>
57 /// </remarks>
58 public virtual void InitializeValidator()
59 {
60 if (this.databaseFile != null)
61 {
62 this.sourceLineNumbers = new SourceLineNumber(databaseFile);
63 }
64 }
65
66 /// <summary>
67 /// Called at the end of the validation of a database file.
68 /// </summary>
69 /// <remarks>
70 /// <para>The default implementation will nullify source lines.</para>
71 /// <para><b>Notes to Inheritors:</b> When overriding
72 /// <b>FinalizeValidator</b> in a derived class, be sure to call
73 /// the base class's <b>FinalizeValidator</b> to thoroughly
74 /// finalize the extension.</para>
75 /// </remarks>
76 public virtual void FinalizeValidator()
77 {
78 this.sourceLineNumbers = null;
79 }
80
81 /// <summary>
82 /// Logs a message from the <see cref="Validator"/>.
83 /// </summary>
84 /// <param name="message">A <see cref="String"/> of tab-delmited tokens
85 /// in the validation message.</param>
86 public virtual void Log(string message)
87 {
88 this.Log(message, null);
89 }
90
91 /// <summary>
92 /// Logs a message from the <see cref="Validator"/>.
93 /// </summary>
94 /// <param name="message">A <see cref="String"/> of tab-delmited tokens
95 /// in the validation message.</param>
96 /// <param name="action">The name of the action to which the message
97 /// belongs.</param>
98 /// <exception cref="ArgumentNullException">The message cannot be null.
99 /// </exception>
100 /// <exception cref="WixException">The message does not contain four (4)
101 /// or more tab-delimited tokens.</exception>
102 /// <remarks>
103 /// <para><paramref name="message"/> a tab-delimited set of tokens,
104 /// formatted according to Windows Installer guidelines for ICE
105 /// message. The following table lists what each token by index
106 /// should mean.</para>
107 /// <para><paramref name="action"/> a name that represents the ICE
108 /// action that was executed (e.g. 'ICE08').</para>
109 /// <list type="table">
110 /// <listheader>
111 /// <term>Index</term>
112 /// <description>Description</description>
113 /// </listheader>
114 /// <item>
115 /// <term>0</term>
116 /// <description>Name of the ICE.</description>
117 /// </item>
118 /// <item>
119 /// <term>1</term>
120 /// <description>Message type. See the following list.</description>
121 /// </item>
122 /// <item>
123 /// <term>2</term>
124 /// <description>Detailed description.</description>
125 /// </item>
126 /// <item>
127 /// <term>3</term>
128 /// <description>Help URL or location.</description>
129 /// </item>
130 /// <item>
131 /// <term>4</term>
132 /// <description>Table name.</description>
133 /// </item>
134 /// <item>
135 /// <term>5</term>
136 /// <description>Column name.</description>
137 /// </item>
138 /// <item>
139 /// <term>6</term>
140 /// <description>This and remaining fields are primary keys
141 /// to identify a row.</description>
142 /// </item>
143 /// </list>
144 /// <para>The message types are one of the following value.</para>
145 /// <list type="table">
146 /// <listheader>
147 /// <term>Value</term>
148 /// <description>Message Type</description>
149 /// </listheader>
150 /// <item>
151 /// <term>0</term>
152 /// <description>Failure message reporting the failure of the
153 /// ICE custom action.</description>
154 /// </item>
155 /// <item>
156 /// <term>1</term>
157 /// <description>Error message reporting database authoring that
158 /// case incorrect behavior.</description>
159 /// </item>
160 /// <item>
161 /// <term>2</term>
162 /// <description>Warning message reporting database authoring that
163 /// causes incorrect behavior in certain cases. Warnings can also
164 /// report unexpected side-effects of database authoring.
165 /// </description>
166 /// </item>
167 /// <item>
168 /// <term>3</term>
169 /// <description>Informational message.</description>
170 /// </item>
171 /// </list>
172 /// </remarks>
173 public virtual void Log(string message, string action)
174 {
175 if (message == null)
176 {
177 throw new ArgumentNullException("message");
178 }
179
180 string[] messageParts = message.Split('\t');
181 if (3 > messageParts.Length)
182 {
183 if (null == action)
184 {
185 throw new WixException(WixErrors.UnexpectedExternalUIMessage(message));
186 }
187 else
188 {
189 throw new WixException(WixErrors.UnexpectedExternalUIMessage(message, action));
190 }
191 }
192
193 SourceLineNumber messageSourceLineNumbers = null;
194 if (6 < messageParts.Length)
195 {
196 string[] primaryKeys = new string[messageParts.Length - 6];
197
198 Array.Copy(messageParts, 6, primaryKeys, 0, primaryKeys.Length);
199
200 messageSourceLineNumbers = this.GetSourceLineNumbers(messageParts[4], primaryKeys);
201 }
202 else // use the file name as the source line information
203 {
204 messageSourceLineNumbers = this.sourceLineNumbers;
205 }
206
207 switch (messageParts[1])
208 {
209 case "0":
210 case "1":
211 this.OnMessage(WixErrors.ValidationError(messageSourceLineNumbers, messageParts[0], messageParts[2]));
212 break;
213 case "2":
214 this.OnMessage(WixWarnings.ValidationWarning(messageSourceLineNumbers, messageParts[0], messageParts[2]));
215 break;
216 case "3":
217 this.OnMessage(WixVerboses.ValidationInfo(messageParts[0], messageParts[2]));
218 break;
219 default:
220 throw new WixException(WixErrors.InvalidValidatorMessageType(messageParts[1]));
221 }
222 }
223
224 /// <summary>
225 /// Gets the source line information (if available) for a row by its table name and primary key.
226 /// </summary>
227 /// <param name="tableName">The table name of the row.</param>
228 /// <param name="primaryKeys">The primary keys of the row.</param>
229 /// <returns>The source line number information if found; null otherwise.</returns>
230 protected SourceLineNumber GetSourceLineNumbers(string tableName, string[] primaryKeys)
231 {
232 // source line information only exists if an output file was supplied
233 if (null != this.output)
234 {
235 // index the source line information if it hasn't been indexed already
236 if (null == this.indexedSourceLineNumbers)
237 {
238 this.indexedSourceLineNumbers = new Hashtable();
239
240 // index each real table
241 foreach (Table table in this.output.Tables)
242 {
243 // skip unreal tables
244 if (table.Definition.Unreal)
245 {
246 continue;
247 }
248
249 // index each row
250 foreach (Row row in table.Rows)
251 {
252 // skip rows that don't contain source line information
253 if (null == row.SourceLineNumbers)
254 {
255 continue;
256 }
257
258 // index the row using its table name and primary key
259 string primaryKey = row.GetPrimaryKey(';');
260 if (null != primaryKey)
261 {
262 string key = String.Concat(table.Name, ":", primaryKey);
263
264 if (this.indexedSourceLineNumbers.ContainsKey(key))
265 {
266 this.OnMessage(WixWarnings.DuplicatePrimaryKey(row.SourceLineNumbers, primaryKey, table.Name));
267 }
268 else
269 {
270 this.indexedSourceLineNumbers.Add(key, row.SourceLineNumbers);
271 }
272 }
273 }
274 }
275 }
276
277 return (SourceLineNumber)this.indexedSourceLineNumbers[String.Concat(tableName, ":", String.Join(";", primaryKeys))];
278 }
279
280 // use the file name as the source line information
281 return this.sourceLineNumbers;
282 }
283
284 /// <summary>
285 /// Sends a message to the <see cref="Message"/> delegate if there is one.
286 /// </summary>
287 /// <param name="e">Message event arguments.</param>
288 /// <remarks>
289 /// <para><b>Notes to Inheritors:</b> When overriding <b>OnMessage</b>
290 /// in a derived class, be sure to call the base class's
291 /// <b>OnMessage</b> method so that registered delegates recieve
292 /// the event.</para>
293 /// </remarks>
294 public virtual void OnMessage(MessageEventArgs e)
295 {
296 Messaging.Instance.OnMessage(e);
297 }
298 }
299}