| /******************************************************************************* |
| * You may amend and distribute as you like, but don't remove this header! |
| * |
| * EPPlus provides server-side generation of Excel 2007/2010 spreadsheets. |
| * See http://www.codeplex.com/EPPlus for details. |
| * |
| * Copyright (C) 2011 Jan Källman |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| * See the GNU Lesser General Public License for more details. |
| * |
| * The GNU Lesser General Public License can be viewed at http://www.opensource.org/licenses/lgpl-license.php |
| * If you unfamiliar with this license or have questions about it, here is an http://www.gnu.org/licenses/gpl-faq.html |
| * |
| * All code and executables are provided "as is" with no warranty either express or implied. |
| * The author accepts no liability for any damage or loss of business that this product may cause. |
| * |
| * Code change notes: |
| * |
| * Author Change Date |
| * ****************************************************************************** |
| * Mats Alm Added 2011-01-01 |
| * Jan Källman License changed GPL-->LGPL 2011-12-27 |
| *******************************************************************************/ |
| |
| using System; |
| using System.Collections.Immutable; |
| using System.Globalization; |
| using System.Text.RegularExpressions; |
| using System.Xml; |
| |
| namespace AppsheetEpplus; |
| |
| /// <summary> |
| /// Excel datavalidation |
| /// </summary> |
| public abstract class ExcelDataValidation : XmlHelper, IExcelDataValidation { |
| private const string _itemElementNodeName = "d:dataValidation"; |
| |
| private readonly string _errorStylePath = "@errorStyle"; |
| private readonly string _errorTitlePath = "@errorTitle"; |
| private readonly string _errorPath = "@error"; |
| private readonly string _promptTitlePath = "@promptTitle"; |
| private readonly string _promptPath = "@prompt"; |
| private readonly string _operatorPath = "@operator"; |
| private readonly string _showErrorMessagePath = "@showErrorMessage"; |
| private readonly string _showInputMessagePath = "@showInputMessage"; |
| private readonly string _typeMessagePath = "@type"; |
| private readonly string _sqrefPath = "@sqref"; |
| private readonly string _allowBlankPath = "@allowBlank"; |
| protected readonly string _formula1Path = "d:formula1"; |
| protected readonly string _formula2Path = "d:formula2"; |
| |
| protected override ImmutableArray<string> SchemaNodeOrder { get; } = [ |
| "type", |
| "errorStyle", |
| "operator", |
| "allowBlank", |
| "showInputMessage", |
| "showErrorMessage", |
| "errorTitle", |
| "error", |
| "promptTitle", |
| "prompt", |
| "sqref", |
| "formula1", |
| "formula2", |
| ]; |
| |
| internal ExcelDataValidation( |
| ExcelWorksheet worksheet, |
| string address, |
| ExcelDataValidationType validationType) |
| : this(worksheet, address, validationType, null) {} |
| |
| /// <summary> |
| /// Constructor |
| /// </summary> |
| /// <param name="worksheet">worksheet that owns the validation</param> |
| /// <param name="itemElementNode">Xml top node (dataValidations)</param> |
| /// <param name="validationType">Data validation type</param> |
| /// <param name="address">address for data validation</param> |
| internal ExcelDataValidation( |
| ExcelWorksheet worksheet, |
| string address, |
| ExcelDataValidationType validationType, |
| XmlNode itemElementNode) |
| : this(worksheet, address, validationType, itemElementNode, null) {} |
| |
| /// <summary> |
| /// Constructor |
| /// </summary> |
| /// <param name="worksheet">worksheet that owns the validation</param> |
| /// <param name="itemElementNode">Xml top node (dataValidations) when importing xml</param> |
| /// <param name="validationType">Data validation type</param> |
| /// <param name="address">address for data validation</param> |
| /// <param name="namespaceManager">Xml Namespace manager</param> |
| internal ExcelDataValidation( |
| ExcelWorksheet worksheet, |
| string address, |
| ExcelDataValidationType validationType, |
| XmlNode itemElementNode, |
| XmlNamespaceManager namespaceManager) |
| : base(namespaceManager ?? worksheet.NameSpaceManager) { |
| ArgumentException.ThrowIfNullOrEmpty(address); |
| address = CheckAndFixRangeAddress(address); |
| if (itemElementNode == null) { |
| //var xmlDoc = worksheet.WorksheetXml; |
| TopNode = worksheet.WorksheetXml.SelectSingleNode( |
| "//d:dataValidations", |
| worksheet.NameSpaceManager); |
| // did not succeed using the XmlHelper methods here... so I'm creating the new node using XmlDocument... |
| var nsUri = NameSpaceManager.LookupNamespace("d"); |
| //itemElementNode = TopNode.OwnerDocument.CreateElement(_itemElementNodeName, nsUri); |
| itemElementNode = TopNode.OwnerDocument.CreateElement( |
| _itemElementNodeName.Split(':')[1], |
| nsUri); |
| TopNode.AppendChild(itemElementNode); |
| } |
| TopNode = itemElementNode; |
| ValidationType = validationType; |
| Address = new(address); |
| } |
| |
| private string CheckAndFixRangeAddress(string address) { |
| if (address.Contains(',')) { |
| throw new FormatException("Multiple addresses may not be commaseparated, use space instead"); |
| } |
| address = address.ToUpper(CultureInfo.InvariantCulture); |
| if (Regex.IsMatch(address, "[A-Z]+:[A-Z]+")) { |
| address = AddressUtility.ParseEntireColumnSelections(address); |
| } |
| return address; |
| } |
| |
| private void SetNullableBoolValue(string path, bool? val) { |
| if (val.HasValue) { |
| SetXmlNodeBool(path, val.Value); |
| } else { |
| DeleteNode(path); |
| } |
| } |
| |
| /// <summary> |
| /// This method will validate the state of the validation |
| /// </summary> |
| /// <exception cref="InvalidOperationException">If the state breaks the rules of the validation</exception> |
| public virtual void Validate() { |
| var address = Address.Address; |
| // validate Formula1 |
| if (string.IsNullOrEmpty(Formula1Internal)) { |
| throw new InvalidOperationException( |
| "Validation of " + address + " failed: Formula1 cannot be empty"); |
| } |
| } |
| |
| /// <summary> |
| /// True if the validation type allows operator to be set. |
| /// </summary> |
| public bool AllowsOperator => ValidationType.AllowOperator; |
| |
| /// <summary> |
| /// Address of data validation |
| /// </summary> |
| public ExcelAddress Address { |
| get => new(GetXmlNodeString(_sqrefPath)); |
| private set { |
| var address = AddressUtility.ParseEntireColumnSelections(value.Address); |
| SetXmlNodeString(_sqrefPath, address); |
| } |
| } |
| |
| /// <summary> |
| /// Validation type |
| /// </summary> |
| public ExcelDataValidationType ValidationType { |
| get { |
| var typeString = GetXmlNodeString(_typeMessagePath); |
| return ExcelDataValidationType.GetBySchemaName(typeString); |
| } |
| private set => SetXmlNodeString(_typeMessagePath, value.SchemaName, true); |
| } |
| |
| /// <summary> |
| /// Operator for comparison between the entered value and Formula/Formulas. |
| /// </summary> |
| public ExcelDataValidationOperator Operator { |
| get { |
| var operatorString = GetXmlNodeString(_operatorPath); |
| return Enum.TryParse<ExcelDataValidationOperator>(operatorString, true, out var op) |
| ? op |
| : ExcelDataValidationOperator.Any; |
| } |
| } |
| |
| /// <summary> |
| /// Warning style |
| /// </summary> |
| public ExcelDataValidationWarningStyle ErrorStyle { |
| get { |
| var errorStyleString = GetXmlNodeString(_errorStylePath); |
| return Enum.TryParse<ExcelDataValidationWarningStyle>(errorStyleString, true, out var style) |
| ? style |
| : ExcelDataValidationWarningStyle.Undefined; |
| } |
| } |
| |
| /// <summary> |
| /// True if blanks should be allowed |
| /// </summary> |
| public bool? AllowBlank { |
| get => GetXmlNodeBoolNullable(_allowBlankPath); |
| set => SetNullableBoolValue(_allowBlankPath, value); |
| } |
| |
| /// <summary> |
| /// True if input message should be shown |
| /// </summary> |
| public bool? ShowInputMessage { |
| get => GetXmlNodeBoolNullable(_showInputMessagePath); |
| set => SetNullableBoolValue(_showInputMessagePath, value); |
| } |
| |
| /// <summary> |
| /// True if error message should be shown |
| /// </summary> |
| public bool? ShowErrorMessage { |
| get => GetXmlNodeBoolNullable(_showErrorMessagePath); |
| set => SetNullableBoolValue(_showErrorMessagePath, value); |
| } |
| |
| /// <summary> |
| /// Title of error message box |
| /// </summary> |
| public string ErrorTitle { |
| get => GetXmlNodeString(_errorTitlePath); |
| set => SetXmlNodeString(_errorTitlePath, value); |
| } |
| |
| /// <summary> |
| /// Error message box text |
| /// </summary> |
| public string Error { |
| get => GetXmlNodeString(_errorPath); |
| set => SetXmlNodeString(_errorPath, value); |
| } |
| |
| public string PromptTitle { |
| get => GetXmlNodeString(_promptTitlePath); |
| set => SetXmlNodeString(_promptTitlePath, value); |
| } |
| |
| public string Prompt { |
| get => GetXmlNodeString(_promptPath); |
| set => SetXmlNodeString(_promptPath, value); |
| } |
| |
| /// <summary> |
| /// Formula 1 |
| /// </summary> |
| protected string Formula1Internal => GetXmlNodeString(_formula1Path); |
| |
| /// <summary> |
| /// Formula 2 |
| /// </summary> |
| protected string Formula2Internal => GetXmlNodeString(_formula2Path); |
| |
| protected void SetValue<T>(T? val, string path) |
| where T : struct { |
| if (!val.HasValue) { |
| DeleteNode(path); |
| } |
| var stringValue = val.Value.ToString().Replace(',', '.'); |
| SetXmlNodeString(path, stringValue); |
| } |
| } |