| /******************************************************************************* | |
| * 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.Generic; | |
| using System.Globalization; | |
| using System.Linq; | |
| using System.Text; | |
| using OfficeOpenXml.Utils; | |
| using System.Xml; | |
| using System.Text.RegularExpressions; | |
| using OfficeOpenXml.DataValidation.Formulas.Contracts; | |
| using OfficeOpenXml.DataValidation.Contracts; | |
| namespace OfficeOpenXml.DataValidation | |
| { | |
| /// <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"; | |
| 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 != null ? namespaceManager : worksheet.NameSpaceManager) | |
| { | |
| Require.Argument(address).IsNotNullOrEmpty("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 ExcelAddress(address); | |
| Init(); | |
| } | |
| private void Init() | |
| { | |
| // set schema node order | |
| SchemaNodeOrder = new string[]{ | |
| "type", | |
| "errorStyle", | |
| "operator", | |
| "allowBlank", | |
| "showInputMessage", | |
| "showErrorMessage", | |
| "errorTitle", | |
| "error", | |
| "promptTitle", | |
| "prompt", | |
| "sqref", | |
| "formula1", | |
| "formula2" | |
| }; | |
| } | |
| 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"); | |
| } | |
| } | |
| #region Public properties | |
| /// <summary> | |
| /// True if the validation type allows operator to be set. | |
| /// </summary> | |
| public bool AllowsOperator | |
| { | |
| get | |
| { | |
| return ValidationType.AllowOperator; | |
| } | |
| } | |
| /// <summary> | |
| /// Address of data validation | |
| /// </summary> | |
| public ExcelAddress Address | |
| { | |
| get | |
| { | |
| return new ExcelAddress(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); | |
| if (!string.IsNullOrEmpty(operatorString)) | |
| { | |
| return (ExcelDataValidationOperator)Enum.Parse(typeof(ExcelDataValidationOperator), operatorString); | |
| } | |
| return default(ExcelDataValidationOperator); | |
| } | |
| set | |
| { | |
| if (!ValidationType.AllowOperator) | |
| { | |
| throw new InvalidOperationException("The current validation type does not allow operator to be set"); | |
| } | |
| SetXmlNodeString(_operatorPath, value.ToString()); | |
| } | |
| } | |
| /// <summary> | |
| /// Warning style | |
| /// </summary> | |
| public ExcelDataValidationWarningStyle ErrorStyle | |
| { | |
| get | |
| { | |
| var errorStyleString = GetXmlNodeString(_errorStylePath); | |
| if (!string.IsNullOrEmpty(errorStyleString)) | |
| { | |
| return (ExcelDataValidationWarningStyle)Enum.Parse(typeof(ExcelDataValidationWarningStyle), errorStyleString); | |
| } | |
| return ExcelDataValidationWarningStyle.undefined; | |
| } | |
| set | |
| { | |
| if (value == ExcelDataValidationWarningStyle.undefined) | |
| { | |
| DeleteNode(_errorStylePath); | |
| } | |
| SetXmlNodeString(_errorStylePath, value.ToString()); | |
| } | |
| } | |
| /// <summary> | |
| /// True if blanks should be allowed | |
| /// </summary> | |
| public bool? AllowBlank | |
| { | |
| get | |
| { | |
| return GetXmlNodeBoolNullable(_allowBlankPath); | |
| } | |
| set | |
| { | |
| SetNullableBoolValue(_allowBlankPath, value); | |
| } | |
| } | |
| /// <summary> | |
| /// True if input message should be shown | |
| /// </summary> | |
| public bool? ShowInputMessage | |
| { | |
| get | |
| { | |
| return GetXmlNodeBoolNullable(_showInputMessagePath); | |
| } | |
| set | |
| { | |
| SetNullableBoolValue(_showInputMessagePath, value); | |
| } | |
| } | |
| /// <summary> | |
| /// True if error message should be shown | |
| /// </summary> | |
| public bool? ShowErrorMessage | |
| { | |
| get | |
| { | |
| return GetXmlNodeBoolNullable(_showErrorMessagePath); | |
| } | |
| set | |
| { | |
| SetNullableBoolValue(_showErrorMessagePath, value); | |
| } | |
| } | |
| /// <summary> | |
| /// Title of error message box | |
| /// </summary> | |
| public string ErrorTitle | |
| { | |
| get | |
| { | |
| return GetXmlNodeString(_errorTitlePath); | |
| } | |
| set | |
| { | |
| SetXmlNodeString(_errorTitlePath, value); | |
| } | |
| } | |
| /// <summary> | |
| /// Error message box text | |
| /// </summary> | |
| public string Error | |
| { | |
| get | |
| { | |
| return GetXmlNodeString(_errorPath); | |
| } | |
| set | |
| { | |
| SetXmlNodeString(_errorPath, value); | |
| } | |
| } | |
| public string PromptTitle | |
| { | |
| get | |
| { | |
| return GetXmlNodeString(_promptTitlePath); | |
| } | |
| set | |
| { | |
| SetXmlNodeString(_promptTitlePath, value); | |
| } | |
| } | |
| public string Prompt | |
| { | |
| get | |
| { | |
| return GetXmlNodeString(_promptPath); | |
| } | |
| set | |
| { | |
| SetXmlNodeString(_promptPath, value); | |
| } | |
| } | |
| /// <summary> | |
| /// Formula 1 | |
| /// </summary> | |
| protected string Formula1Internal | |
| { | |
| get | |
| { | |
| return GetXmlNodeString(_formula1Path); | |
| } | |
| } | |
| /// <summary> | |
| /// Formula 2 | |
| /// </summary> | |
| protected string Formula2Internal | |
| { | |
| get | |
| { | |
| return GetXmlNodeString(_formula2Path); | |
| } | |
| } | |
| #endregion | |
| protected void SetValue<T>(Nullable<T> val, string path) | |
| where T : struct | |
| { | |
| if (!val.HasValue) | |
| { | |
| DeleteNode(path); | |
| } | |
| var stringValue = val.Value.ToString().Replace(',', '.'); | |
| SetXmlNodeString(path, stringValue); | |
| } | |
| } | |
| } |