|  | /******************************************************************************* | 
|  | * 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 | 
|  | * Mats Alm                         Applying patch submitted    2011-11-14 | 
|  | *                                  by Ted Heatherington | 
|  | * Jan Källman		                License changed GPL-->LGPL  2011-12-27 | 
|  | * Raziq York		                Added support for Any type  2014-08-08 | 
|  | *******************************************************************************/ | 
|  | using System; | 
|  | using System.Collections.Generic; | 
|  | using System.Linq; | 
|  | using System.Text; | 
|  | using System.Collections; | 
|  | using OfficeOpenXml.Utils; | 
|  | using System.Xml; | 
|  | using OfficeOpenXml.DataValidation.Contracts; | 
|  |  | 
|  | namespace OfficeOpenXml.DataValidation | 
|  | { | 
|  | /// <summary> | 
|  | /// <para> | 
|  | /// Collection of <see cref="ExcelDataValidation"/>. This class is providing the API for EPPlus data validation. | 
|  | /// </para> | 
|  | /// <para> | 
|  | /// The public methods of this class (Add[...]Validation) will create a datavalidation entry in the worksheet. When this | 
|  | /// validation has been created changes to the properties will affect the workbook immediately. | 
|  | /// </para> | 
|  | /// <para> | 
|  | /// Each type of validation has either a formula or a typed value/values, except for custom validation which has a formula only. | 
|  | /// </para> | 
|  | /// <code> | 
|  | /// // Add a date time validation | 
|  | /// var validation = worksheet.DataValidation.AddDateTimeValidation("A1"); | 
|  | /// // set validation properties | 
|  | /// validation.ShowErrorMessage = true; | 
|  | /// validation.ErrorTitle = "An invalid date was entered"; | 
|  | /// validation.Error = "The date must be between 2011-01-31 and 2011-12-31"; | 
|  | /// validation.Prompt = "Enter date here"; | 
|  | /// validation.Formula.Value = DateTime.Parse("2011-01-01"); | 
|  | /// validation.Formula2.Value = DateTime.Parse("2011-12-31"); | 
|  | /// validation.Operator = ExcelDataValidationOperator.between; | 
|  | /// </code> | 
|  | /// </summary> | 
|  | public class ExcelDataValidationCollection : XmlHelper, IEnumerable<IExcelDataValidation> | 
|  | { | 
|  | private List<IExcelDataValidation> _validations = new List<IExcelDataValidation>(); | 
|  | private ExcelWorksheet _worksheet = null; | 
|  |  | 
|  | private const string DataValidationPath = "//d:dataValidations"; | 
|  | private readonly string DataValidationItemsPath = string.Format("{0}/d:dataValidation", DataValidationPath); | 
|  |  | 
|  | /// <summary> | 
|  | /// Constructor | 
|  | /// </summary> | 
|  | /// <param name="worksheet"></param> | 
|  | internal ExcelDataValidationCollection(ExcelWorksheet worksheet) | 
|  | : base(worksheet.NameSpaceManager, worksheet.WorksheetXml.DocumentElement) | 
|  | { | 
|  | Require.Argument(worksheet).IsNotNull("worksheet"); | 
|  | _worksheet = worksheet; | 
|  | SchemaNodeOrder = worksheet.SchemaNodeOrder; | 
|  |  | 
|  | // check existing nodes and load them | 
|  | var dataValidationNodes = worksheet.WorksheetXml.SelectNodes(DataValidationItemsPath, worksheet.NameSpaceManager); | 
|  | if (dataValidationNodes != null && dataValidationNodes.Count > 0) | 
|  | { | 
|  | foreach (XmlNode node in dataValidationNodes) | 
|  | { | 
|  | if (node.Attributes["sqref"] == null) continue; | 
|  |  | 
|  | var addr = node.Attributes["sqref"].Value; | 
|  |  | 
|  | var typeSchema = node.Attributes["type"] != null ? node.Attributes["type"].Value : ""; | 
|  |  | 
|  | var type = ExcelDataValidationType.GetBySchemaName(typeSchema); | 
|  | _validations.Add(ExcelDataValidationFactory.Create(type, worksheet, addr, node)); | 
|  | } | 
|  | } | 
|  | if (_validations.Count > 0) | 
|  | { | 
|  | OnValidationCountChanged(); | 
|  | } | 
|  | } | 
|  |  | 
|  | private void EnsureRootElementExists() | 
|  | { | 
|  | var node = _worksheet.WorksheetXml.SelectSingleNode(DataValidationPath, _worksheet.NameSpaceManager); | 
|  | if (node == null) | 
|  | { | 
|  | CreateNode(DataValidationPath.TrimStart('/')); | 
|  | } | 
|  | } | 
|  |  | 
|  | private void OnValidationCountChanged() | 
|  | { | 
|  | //if (TopNode != null) | 
|  | //{ | 
|  | //    SetXmlNodeString("@count", _validations.Count.ToString()); | 
|  | //} | 
|  | } | 
|  |  | 
|  | private XmlNode GetRootNode() | 
|  | { | 
|  | EnsureRootElementExists(); | 
|  | TopNode = _worksheet.WorksheetXml.SelectSingleNode(DataValidationPath, _worksheet.NameSpaceManager); | 
|  | return TopNode; | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Validates address - not empty, collisions | 
|  | /// </summary> | 
|  | /// <param name="address"></param> | 
|  | /// <param name="validatingValidation"></param> | 
|  | private void ValidateAddress(string address, IExcelDataValidation validatingValidation) | 
|  | { | 
|  | Require.Argument(address).IsNotNullOrEmpty("address"); | 
|  |  | 
|  | // ensure that the new address does not collide with an existing validation. | 
|  | var newAddress = new ExcelAddress(address); | 
|  | if (_validations.Count > 0) | 
|  | { | 
|  | foreach (var validation in _validations) | 
|  | { | 
|  | if (validatingValidation != null && validatingValidation == validation) | 
|  | { | 
|  | continue; | 
|  | } | 
|  | var result = validation.Address.Collide(newAddress); | 
|  | if (result != ExcelAddressBase.eAddressCollition.No) | 
|  | { | 
|  | throw new InvalidOperationException(string.Format("The address ({0}) collides with an existing validation ({1})", address, validation.Address.Address)); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | private void ValidateAddress(string address) | 
|  | { | 
|  | ValidateAddress(address, null); | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Validates all data validations. | 
|  | /// </summary> | 
|  | internal void ValidateAll() | 
|  | { | 
|  | foreach (var validation in _validations) | 
|  | { | 
|  | validation.Validate(); | 
|  |  | 
|  | ValidateAddress(validation.Address.Address, validation); | 
|  | } | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Adds a <see cref="ExcelDataValidationAny"/> to the worksheet. | 
|  | /// </summary> | 
|  | /// <param name="address">The range/address to validate</param> | 
|  | /// <returns></returns> | 
|  | public IExcelDataValidationAny AddAnyValidation(string address) | 
|  | { | 
|  | ValidateAddress(address); | 
|  | EnsureRootElementExists(); | 
|  | var item = new ExcelDataValidationAny(_worksheet, address, ExcelDataValidationType.Any); | 
|  | _validations.Add(item); | 
|  | OnValidationCountChanged(); | 
|  | return item; | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Adds an <see cref="IExcelDataValidationInt"/> to the worksheet. Whole means that the only accepted values | 
|  | /// are integer values. | 
|  | /// </summary> | 
|  | /// <param name="address">the range/address to validate</param> | 
|  | public IExcelDataValidationInt AddIntegerValidation(string address) | 
|  | { | 
|  | ValidateAddress(address); | 
|  | EnsureRootElementExists(); | 
|  | var item = new ExcelDataValidationInt(_worksheet, address, ExcelDataValidationType.Whole); | 
|  | _validations.Add(item); | 
|  | OnValidationCountChanged(); | 
|  | return item; | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Addes an <see cref="IExcelDataValidationDecimal"/> to the worksheet. The only accepted values are | 
|  | /// decimal values. | 
|  | /// </summary> | 
|  | /// <param name="address">The range/address to validate</param> | 
|  | /// <returns></returns> | 
|  | public IExcelDataValidationDecimal AddDecimalValidation(string address) | 
|  | { | 
|  | ValidateAddress(address); | 
|  | EnsureRootElementExists(); | 
|  | var item = new ExcelDataValidationDecimal(_worksheet, address, ExcelDataValidationType.Decimal); | 
|  | _validations.Add(item); | 
|  | OnValidationCountChanged(); | 
|  | return item; | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Adds an <see cref="IExcelDataValidationList"/> to the worksheet. The accepted values are defined | 
|  | /// in a list. | 
|  | /// </summary> | 
|  | /// <param name="address">The range/address to validate</param> | 
|  | /// <returns></returns> | 
|  | public IExcelDataValidationList AddListValidation(string address) | 
|  | { | 
|  | ValidateAddress(address); | 
|  | EnsureRootElementExists(); | 
|  | var item = new ExcelDataValidationList(_worksheet, address, ExcelDataValidationType.List); | 
|  | _validations.Add(item); | 
|  | OnValidationCountChanged(); | 
|  | return item; | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Adds an <see cref="IExcelDataValidationInt"/> regarding text length to the worksheet. | 
|  | /// </summary> | 
|  | /// <param name="address">The range/address to validate</param> | 
|  | /// <returns></returns> | 
|  | public IExcelDataValidationInt AddTextLengthValidation(string address) | 
|  | { | 
|  | ValidateAddress(address); | 
|  | EnsureRootElementExists(); | 
|  | var item = new ExcelDataValidationInt(_worksheet, address, ExcelDataValidationType.TextLength); | 
|  | _validations.Add(item); | 
|  | OnValidationCountChanged(); | 
|  | return item; | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Adds an <see cref="IExcelDataValidationDateTime"/> to the worksheet. | 
|  | /// </summary> | 
|  | /// <param name="address">The range/address to validate</param> | 
|  | /// <returns></returns> | 
|  | public IExcelDataValidationDateTime AddDateTimeValidation(string address) | 
|  | { | 
|  | ValidateAddress(address); | 
|  | EnsureRootElementExists(); | 
|  | var item = new ExcelDataValidationDateTime(_worksheet, address, ExcelDataValidationType.DateTime); | 
|  | _validations.Add(item); | 
|  | OnValidationCountChanged(); | 
|  | return item; | 
|  | } | 
|  |  | 
|  |  | 
|  | public IExcelDataValidationTime AddTimeValidation(string address) | 
|  | { | 
|  | ValidateAddress(address); | 
|  | EnsureRootElementExists(); | 
|  | var item = new ExcelDataValidationTime(_worksheet, address, ExcelDataValidationType.Time); | 
|  | _validations.Add(item); | 
|  | OnValidationCountChanged(); | 
|  | return item; | 
|  | } | 
|  | /// <summary> | 
|  | /// Adds a <see cref="ExcelDataValidationCustom"/> to the worksheet. | 
|  | /// </summary> | 
|  | /// <param name="address">The range/address to validate</param> | 
|  | /// <returns></returns> | 
|  | public IExcelDataValidationCustom AddCustomValidation(string address) | 
|  | { | 
|  | ValidateAddress(address); | 
|  | EnsureRootElementExists(); | 
|  | var item = new ExcelDataValidationCustom(_worksheet, address, ExcelDataValidationType.Custom); | 
|  | _validations.Add(item); | 
|  | OnValidationCountChanged(); | 
|  | return item; | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Removes an <see cref="ExcelDataValidation"/> from the collection. | 
|  | /// </summary> | 
|  | /// <param name="item">The item to remove</param> | 
|  | /// <returns>True if remove succeeds, otherwise false</returns> | 
|  | /// <exception cref="ArgumentNullException">if <paramref name="item"/> is null</exception> | 
|  | public bool Remove(IExcelDataValidation item) | 
|  | { | 
|  | if (!(item is ExcelDataValidation)) | 
|  | { | 
|  | throw new InvalidCastException("The supplied item must inherit OfficeOpenXml.DataValidation.ExcelDataValidation"); | 
|  | } | 
|  | Require.Argument(item).IsNotNull("item"); | 
|  | TopNode.RemoveChild(((ExcelDataValidation)item).TopNode); | 
|  | var retVal = _validations.Remove(item); | 
|  | if (retVal) OnValidationCountChanged(); | 
|  | return retVal; | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Number of validations | 
|  | /// </summary> | 
|  | public int Count | 
|  | { | 
|  | get { return _validations.Count; } | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Index operator, returns by 0-based index | 
|  | /// </summary> | 
|  | /// <param name="index"></param> | 
|  | /// <returns></returns> | 
|  | public IExcelDataValidation this[int index] | 
|  | { | 
|  | get { return _validations[index]; } | 
|  | set { _validations[index] = value; } | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Index operator, returns a data validation which address partly or exactly matches the searched address. | 
|  | /// </summary> | 
|  | /// <param name="address">A cell address or range</param> | 
|  | /// <returns>A <see cref="ExcelDataValidation"/> or null if no match</returns> | 
|  | public IExcelDataValidation this[string address] | 
|  | { | 
|  | get | 
|  | { | 
|  | var searchedAddress = new ExcelAddress(address); | 
|  | return _validations.Find(x => x.Address.Collide(searchedAddress) != ExcelAddressBase.eAddressCollition.No); | 
|  | } | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Returns all validations that matches the supplied predicate <paramref name="match"/>. | 
|  | /// </summary> | 
|  | /// <param name="match">predicate to filter out matching validations</param> | 
|  | /// <returns></returns> | 
|  | public IEnumerable<IExcelDataValidation> FindAll(Predicate<IExcelDataValidation> match) | 
|  | { | 
|  | return _validations.FindAll(match); | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Returns the first matching validation. | 
|  | /// </summary> | 
|  | /// <param name="match"></param> | 
|  | /// <returns></returns> | 
|  | public IExcelDataValidation Find(Predicate<IExcelDataValidation> match) | 
|  | { | 
|  | return _validations.Find(match); | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Removes all validations from the collection. | 
|  | /// </summary> | 
|  | public void Clear() | 
|  | { | 
|  | DeleteAllNode(DataValidationItemsPath.TrimStart('/')); | 
|  | _validations.Clear(); | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Removes the validations that matches the predicate | 
|  | /// </summary> | 
|  | /// <param name="match"></param> | 
|  | public void RemoveAll(Predicate<IExcelDataValidation> match) | 
|  | { | 
|  | var matches = _validations.FindAll(match); | 
|  | foreach (var m in matches) | 
|  | { | 
|  | if (!(m is ExcelDataValidation)) | 
|  | { | 
|  | throw new InvalidCastException("The supplied item must inherit OfficeOpenXml.DataValidation.ExcelDataValidation"); | 
|  | } | 
|  | TopNode.SelectSingleNode(DataValidationPath.TrimStart('/'), NameSpaceManager).RemoveChild(((ExcelDataValidation)m).TopNode); | 
|  | } | 
|  | _validations.RemoveAll(match); | 
|  | OnValidationCountChanged(); | 
|  | } | 
|  |  | 
|  | IEnumerator<IExcelDataValidation> IEnumerable<IExcelDataValidation>.GetEnumerator() | 
|  | { | 
|  | return _validations.GetEnumerator(); | 
|  | } | 
|  |  | 
|  | IEnumerator System.Collections.IEnumerable.GetEnumerator() | 
|  | { | 
|  | return _validations.GetEnumerator(); | 
|  | } | 
|  | } | 
|  | } |