/*******************************************************************************
* 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.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
{
///
/// Excel datavalidation
///
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)
{ }
///
/// Constructor
///
/// worksheet that owns the validation
/// Xml top node (dataValidations)
/// Data validation type
/// address for data validation
internal ExcelDataValidation(ExcelWorksheet worksheet, string address, ExcelDataValidationType validationType, XmlNode itemElementNode)
: this(worksheet, address, validationType, itemElementNode, null)
{
}
///
/// Constructor
///
/// worksheet that owns the validation
/// Xml top node (dataValidations) when importing xml
/// Data validation type
/// address for data validation
/// Xml Namespace manager
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);
if (validationType.AllowOperator)
{
Operator = ExcelDataValidationOperator.any;
}
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();
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);
}
}
///
/// This method will validate the state of the validation
///
/// If the state breaks the rules of the validation
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
///
/// True if the validation type allows operator to be set.
///
public bool AllowsOperator
{
get
{
return ValidationType.AllowOperator;
}
}
///
/// Address of data validation
///
public ExcelAddress Address
{
get
{
return new ExcelAddress(GetXmlNodeString(_sqrefPath));
}
private set
{
var address = AddressUtility.ParseEntireColumnSelections(value.Address);
SetXmlNodeString(_sqrefPath, address);
}
}
///
/// Validation type
///
public ExcelDataValidationType ValidationType
{
get
{
var typeString = GetXmlNodeString(_typeMessagePath);
return ExcelDataValidationType.GetBySchemaName(typeString);
}
private set
{
SetXmlNodeString(_typeMessagePath, value.SchemaName);
}
}
///
/// Operator for comparison between the entered value and Formula/Formulas.
///
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());
}
}
///
/// Warning style
///
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());
}
}
///
/// True if blanks should be allowed
///
public bool? AllowBlank
{
get
{
return GetXmlNodeBoolNullable(_allowBlankPath);
}
set
{
SetNullableBoolValue(_allowBlankPath, value);
}
}
///
/// True if input message should be shown
///
public bool? ShowInputMessage
{
get
{
return GetXmlNodeBoolNullable(_showInputMessagePath);
}
set
{
SetNullableBoolValue(_showInputMessagePath, value);
}
}
///
/// True if error message should be shown
///
public bool? ShowErrorMessage
{
get
{
return GetXmlNodeBoolNullable(_showErrorMessagePath);
}
set
{
SetNullableBoolValue(_showErrorMessagePath, value);
}
}
///
/// Title of error message box
///
public string ErrorTitle
{
get
{
return GetXmlNodeString(_errorTitlePath);
}
set
{
SetXmlNodeString(_errorTitlePath, value);
}
}
///
/// Error message box text
///
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);
}
}
///
/// Formula 1
///
protected string Formula1Internal
{
get
{
return GetXmlNodeString(_formula1Path);
}
}
///
/// Formula 2
///
protected string Formula2Internal
{
get
{
return GetXmlNodeString(_formula2Path);
}
}
#endregion
protected void SetValue(Nullable val, string path)
where T : struct
{
if (!val.HasValue)
{
DeleteNode(path);
}
var stringValue = val.Value.ToString().Replace(',', '.');
SetXmlNodeString(path, stringValue);
}
}
}