blob: 4ad68e0de03be3e432a02d2ed4b16496d1313eea [file] [log] [blame]
/*******************************************************************************
* 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.Immutable;
using System.Globalization;
using System.Text.RegularExpressions;
using System.Xml;
namespace AppsheetEpplus;
/// <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";
protected override ImmutableArray<string> SchemaNodeOrder { get; } = [
"type",
"errorStyle",
"operator",
"allowBlank",
"showInputMessage",
"showErrorMessage",
"errorTitle",
"error",
"promptTitle",
"prompt",
"sqref",
"formula1",
"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 ?? worksheet.NameSpaceManager) {
ArgumentException.ThrowIfNullOrEmpty(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(address);
}
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");
}
}
/// <summary>
/// True if the validation type allows operator to be set.
/// </summary>
public bool AllowsOperator => ValidationType.AllowOperator;
/// <summary>
/// Address of data validation
/// </summary>
public ExcelAddress Address {
get => new(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);
return Enum.TryParse<ExcelDataValidationOperator>(operatorString, true, out var op)
? op
: ExcelDataValidationOperator.Any;
}
}
/// <summary>
/// Warning style
/// </summary>
public ExcelDataValidationWarningStyle ErrorStyle {
get {
var errorStyleString = GetXmlNodeString(_errorStylePath);
return Enum.TryParse<ExcelDataValidationWarningStyle>(errorStyleString, true, out var style)
? style
: ExcelDataValidationWarningStyle.Undefined;
}
}
/// <summary>
/// True if blanks should be allowed
/// </summary>
public bool? AllowBlank {
get => GetXmlNodeBoolNullable(_allowBlankPath);
set => SetNullableBoolValue(_allowBlankPath, value);
}
/// <summary>
/// True if input message should be shown
/// </summary>
public bool? ShowInputMessage {
get => GetXmlNodeBoolNullable(_showInputMessagePath);
set => SetNullableBoolValue(_showInputMessagePath, value);
}
/// <summary>
/// True if error message should be shown
/// </summary>
public bool? ShowErrorMessage {
get => GetXmlNodeBoolNullable(_showErrorMessagePath);
set => SetNullableBoolValue(_showErrorMessagePath, value);
}
/// <summary>
/// Title of error message box
/// </summary>
public string ErrorTitle {
get => GetXmlNodeString(_errorTitlePath);
set => SetXmlNodeString(_errorTitlePath, value);
}
/// <summary>
/// Error message box text
/// </summary>
public string Error {
get => GetXmlNodeString(_errorPath);
set => SetXmlNodeString(_errorPath, value);
}
public string PromptTitle {
get => GetXmlNodeString(_promptTitlePath);
set => SetXmlNodeString(_promptTitlePath, value);
}
public string Prompt {
get => GetXmlNodeString(_promptPath);
set => SetXmlNodeString(_promptPath, value);
}
/// <summary>
/// Formula 1
/// </summary>
protected string Formula1Internal => GetXmlNodeString(_formula1Path);
/// <summary>
/// Formula 2
/// </summary>
protected string Formula2Internal => GetXmlNodeString(_formula2Path);
protected void SetValue<T>(T? val, string path)
where T : struct {
if (!val.HasValue) {
DeleteNode(path);
}
var stringValue = val.Value.ToString().Replace(',', '.');
SetXmlNodeString(path, stringValue);
}
}