/*******************************************************************************
* 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
{
///
///
/// Collection of . This class is providing the API for EPPlus data validation.
///
///
/// 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.
///
///
/// Each type of validation has either a formula or a typed value/values, except for custom validation which has a formula only.
///
///
/// // 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;
///
///
public class ExcelDataValidationCollection : XmlHelper, IEnumerable
{
private List _validations = new List();
private ExcelWorksheet _worksheet = null;
private const string DataValidationPath = "//d:dataValidations";
private readonly string DataValidationItemsPath = string.Format("{0}/d:dataValidation", DataValidationPath);
///
/// Constructor
///
///
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;
}
///
/// Validates address - not empty, collisions
///
///
///
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);
}
///
/// Validates all data validations.
///
internal void ValidateAll()
{
foreach (var validation in _validations)
{
validation.Validate();
ValidateAddress(validation.Address.Address, validation);
}
}
///
/// Adds a to the worksheet.
///
/// The range/address to validate
///
public IExcelDataValidationAny AddAnyValidation(string address)
{
ValidateAddress(address);
EnsureRootElementExists();
var item = new ExcelDataValidationAny(_worksheet, address, ExcelDataValidationType.Any);
_validations.Add(item);
OnValidationCountChanged();
return item;
}
///
/// Adds an to the worksheet. Whole means that the only accepted values
/// are integer values.
///
/// the range/address to validate
public IExcelDataValidationInt AddIntegerValidation(string address)
{
ValidateAddress(address);
EnsureRootElementExists();
var item = new ExcelDataValidationInt(_worksheet, address, ExcelDataValidationType.Whole);
_validations.Add(item);
OnValidationCountChanged();
return item;
}
///
/// Addes an to the worksheet. The only accepted values are
/// decimal values.
///
/// The range/address to validate
///
public IExcelDataValidationDecimal AddDecimalValidation(string address)
{
ValidateAddress(address);
EnsureRootElementExists();
var item = new ExcelDataValidationDecimal(_worksheet, address, ExcelDataValidationType.Decimal);
_validations.Add(item);
OnValidationCountChanged();
return item;
}
///
/// Adds an to the worksheet. The accepted values are defined
/// in a list.
///
/// The range/address to validate
///
public IExcelDataValidationList AddListValidation(string address)
{
ValidateAddress(address);
EnsureRootElementExists();
var item = new ExcelDataValidationList(_worksheet, address, ExcelDataValidationType.List);
_validations.Add(item);
OnValidationCountChanged();
return item;
}
///
/// Adds an regarding text length to the worksheet.
///
/// The range/address to validate
///
public IExcelDataValidationInt AddTextLengthValidation(string address)
{
ValidateAddress(address);
EnsureRootElementExists();
var item = new ExcelDataValidationInt(_worksheet, address, ExcelDataValidationType.TextLength);
_validations.Add(item);
OnValidationCountChanged();
return item;
}
///
/// Adds an to the worksheet.
///
/// The range/address to validate
///
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;
}
///
/// Adds a to the worksheet.
///
/// The range/address to validate
///
public IExcelDataValidationCustom AddCustomValidation(string address)
{
ValidateAddress(address);
EnsureRootElementExists();
var item = new ExcelDataValidationCustom(_worksheet, address, ExcelDataValidationType.Custom);
_validations.Add(item);
OnValidationCountChanged();
return item;
}
///
/// Removes an from the collection.
///
/// The item to remove
/// True if remove succeeds, otherwise false
/// if is null
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;
}
///
/// Number of validations
///
public int Count
{
get { return _validations.Count; }
}
///
/// Index operator, returns by 0-based index
///
///
///
public IExcelDataValidation this[int index]
{
get { return _validations[index]; }
set { _validations[index] = value; }
}
///
/// Index operator, returns a data validation which address partly or exactly matches the searched address.
///
/// A cell address or range
/// A or null if no match
public IExcelDataValidation this[string address]
{
get
{
var searchedAddress = new ExcelAddress(address);
return _validations.Find(x => x.Address.Collide(searchedAddress) != ExcelAddressBase.eAddressCollition.No);
}
}
///
/// Returns all validations that matches the supplied predicate .
///
/// predicate to filter out matching validations
///
public IEnumerable FindAll(Predicate match)
{
return _validations.FindAll(match);
}
///
/// Returns the first matching validation.
///
///
///
public IExcelDataValidation Find(Predicate match)
{
return _validations.Find(match);
}
///
/// Removes all validations from the collection.
///
public void Clear()
{
DeleteAllNode(DataValidationItemsPath.TrimStart('/'));
_validations.Clear();
}
///
/// Removes the validations that matches the predicate
///
///
public void RemoveAll(Predicate 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 IEnumerable.GetEnumerator()
{
return _validations.GetEnumerator();
}
IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return _validations.GetEnumerator();
}
}
}