From dbde9e7104b907bbbaea17e21247d8cafc8b3a4c Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Sat, 14 Oct 2017 16:12:07 -0700 Subject: Massive refactoring to introduce the concept of IBackend --- .../ValidatorExtension.cs | 299 +++++++++++++++++++++ 1 file changed, 299 insertions(+) create mode 100644 src/WixToolset.Core.WindowsInstaller/ValidatorExtension.cs (limited to 'src/WixToolset.Core.WindowsInstaller/ValidatorExtension.cs') 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 @@ +// 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. + +namespace WixToolset.Extensibility +{ + using System; + using System.Collections; + using WixToolset.Data; + + /// + /// Base class for creating a validator extension. This default implementation + /// will fire and event with the ICE name and description. + /// + public class ValidatorExtension : IMessageHandler + { + private string databaseFile; + private Hashtable indexedSourceLineNumbers; + private Output output; + private SourceLineNumber sourceLineNumbers; + + /// + /// Instantiate a new . + /// + public ValidatorExtension() + { + } + + /// + /// Gets or sets the path to the database to validate. + /// + /// The path to the database to validate. + public string DatabaseFile + { + get { return this.databaseFile; } + set { this.databaseFile = value; } + } + + /// + /// Gets or sets the for finding source line information. + /// + /// The for finding source line information. + public Output Output + { + get { return this.output; } + set { this.output = value; } + } + + /// + /// Called at the beginning of the validation of a database file. + /// + /// + /// The will set + /// before calling InitializeValidator. + /// Notes to Inheritors: When overriding + /// InitializeValidator in a derived class, be sure to call + /// the base class's InitializeValidator to thoroughly + /// initialize the extension. + /// + public virtual void InitializeValidator() + { + if (this.databaseFile != null) + { + this.sourceLineNumbers = new SourceLineNumber(databaseFile); + } + } + + /// + /// Called at the end of the validation of a database file. + /// + /// + /// The default implementation will nullify source lines. + /// Notes to Inheritors: When overriding + /// FinalizeValidator in a derived class, be sure to call + /// the base class's FinalizeValidator to thoroughly + /// finalize the extension. + /// + public virtual void FinalizeValidator() + { + this.sourceLineNumbers = null; + } + + /// + /// Logs a message from the . + /// + /// A of tab-delmited tokens + /// in the validation message. + public virtual void Log(string message) + { + this.Log(message, null); + } + + /// + /// Logs a message from the . + /// + /// A of tab-delmited tokens + /// in the validation message. + /// The name of the action to which the message + /// belongs. + /// The message cannot be null. + /// + /// The message does not contain four (4) + /// or more tab-delimited tokens. + /// + /// a tab-delimited set of tokens, + /// formatted according to Windows Installer guidelines for ICE + /// message. The following table lists what each token by index + /// should mean. + /// a name that represents the ICE + /// action that was executed (e.g. 'ICE08'). + /// + /// + /// Index + /// Description + /// + /// + /// 0 + /// Name of the ICE. + /// + /// + /// 1 + /// Message type. See the following list. + /// + /// + /// 2 + /// Detailed description. + /// + /// + /// 3 + /// Help URL or location. + /// + /// + /// 4 + /// Table name. + /// + /// + /// 5 + /// Column name. + /// + /// + /// 6 + /// This and remaining fields are primary keys + /// to identify a row. + /// + /// + /// The message types are one of the following value. + /// + /// + /// Value + /// Message Type + /// + /// + /// 0 + /// Failure message reporting the failure of the + /// ICE custom action. + /// + /// + /// 1 + /// Error message reporting database authoring that + /// case incorrect behavior. + /// + /// + /// 2 + /// Warning message reporting database authoring that + /// causes incorrect behavior in certain cases. Warnings can also + /// report unexpected side-effects of database authoring. + /// + /// + /// + /// 3 + /// Informational message. + /// + /// + /// + public virtual void Log(string message, string action) + { + if (message == null) + { + throw new ArgumentNullException("message"); + } + + string[] messageParts = message.Split('\t'); + if (3 > messageParts.Length) + { + if (null == action) + { + throw new WixException(WixErrors.UnexpectedExternalUIMessage(message)); + } + else + { + throw new WixException(WixErrors.UnexpectedExternalUIMessage(message, action)); + } + } + + SourceLineNumber messageSourceLineNumbers = null; + if (6 < messageParts.Length) + { + string[] primaryKeys = new string[messageParts.Length - 6]; + + Array.Copy(messageParts, 6, primaryKeys, 0, primaryKeys.Length); + + messageSourceLineNumbers = this.GetSourceLineNumbers(messageParts[4], primaryKeys); + } + else // use the file name as the source line information + { + messageSourceLineNumbers = this.sourceLineNumbers; + } + + switch (messageParts[1]) + { + case "0": + case "1": + this.OnMessage(WixErrors.ValidationError(messageSourceLineNumbers, messageParts[0], messageParts[2])); + break; + case "2": + this.OnMessage(WixWarnings.ValidationWarning(messageSourceLineNumbers, messageParts[0], messageParts[2])); + break; + case "3": + this.OnMessage(WixVerboses.ValidationInfo(messageParts[0], messageParts[2])); + break; + default: + throw new WixException(WixErrors.InvalidValidatorMessageType(messageParts[1])); + } + } + + /// + /// Gets the source line information (if available) for a row by its table name and primary key. + /// + /// The table name of the row. + /// The primary keys of the row. + /// The source line number information if found; null otherwise. + protected SourceLineNumber GetSourceLineNumbers(string tableName, string[] primaryKeys) + { + // source line information only exists if an output file was supplied + if (null != this.output) + { + // index the source line information if it hasn't been indexed already + if (null == this.indexedSourceLineNumbers) + { + this.indexedSourceLineNumbers = new Hashtable(); + + // index each real table + foreach (Table table in this.output.Tables) + { + // skip unreal tables + if (table.Definition.Unreal) + { + continue; + } + + // index each row + foreach (Row row in table.Rows) + { + // skip rows that don't contain source line information + if (null == row.SourceLineNumbers) + { + continue; + } + + // index the row using its table name and primary key + string primaryKey = row.GetPrimaryKey(';'); + if (null != primaryKey) + { + string key = String.Concat(table.Name, ":", primaryKey); + + if (this.indexedSourceLineNumbers.ContainsKey(key)) + { + this.OnMessage(WixWarnings.DuplicatePrimaryKey(row.SourceLineNumbers, primaryKey, table.Name)); + } + else + { + this.indexedSourceLineNumbers.Add(key, row.SourceLineNumbers); + } + } + } + } + } + + return (SourceLineNumber)this.indexedSourceLineNumbers[String.Concat(tableName, ":", String.Join(";", primaryKeys))]; + } + + // use the file name as the source line information + return this.sourceLineNumbers; + } + + /// + /// Sends a message to the delegate if there is one. + /// + /// Message event arguments. + /// + /// Notes to Inheritors: When overriding OnMessage + /// in a derived class, be sure to call the base class's + /// OnMessage method so that registered delegates recieve + /// the event. + /// + public virtual void OnMessage(MessageEventArgs e) + { + Messaging.Instance.OnMessage(e); + } + } +} -- cgit v1.2.3-55-g6feb