| /******************************************************************************* | 
 |  * 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); | 
 |         } | 
 |     } | 
 | } |