excelize/datavalidation.go

274 lines
8.5 KiB
Go
Raw Normal View History

// Copyright 2016 - 2021 The excelize Authors. All rights reserved. Use of
2018-09-14 00:44:23 +08:00
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
2020-06-22 00:14:56 +08:00
// and read from XLSX / XLSM / XLTM files. Supports reading and writing
// spreadsheet documents generated by Microsoft Excel™ 2007 and later. Supports
2020-06-22 00:14:56 +08:00
// complex components by high compatibility, and provided streaming API for
// generating or reading data from a worksheet with huge amounts of data. This
// library needs Go version 1.15 or later.
2018-09-14 00:58:48 +08:00
2018-07-30 22:09:41 +08:00
package excelize
import (
"fmt"
"math"
2018-07-30 22:09:41 +08:00
"strings"
"unicode/utf16"
2018-07-30 22:09:41 +08:00
)
2018-09-01 23:32:44 +08:00
// DataValidationType defined the type of data validation.
2018-07-30 22:09:41 +08:00
type DataValidationType int
2018-09-01 23:32:44 +08:00
// Data validation types.
2018-07-30 22:09:41 +08:00
const (
_DataValidationType = iota
2018-09-01 23:32:44 +08:00
typeNone // inline use
2018-07-30 22:09:41 +08:00
DataValidationTypeCustom
DataValidationTypeDate
DataValidationTypeDecimal
2018-09-01 23:32:44 +08:00
typeList // inline use
2018-07-30 22:09:41 +08:00
DataValidationTypeTextLeng
DataValidationTypeTime
// DataValidationTypeWhole Integer
DataValidationTypeWhole
)
const (
// dataValidationFormulaStrLen 255 characters
dataValidationFormulaStrLen = 255
2018-07-30 22:09:41 +08:00
)
2018-09-01 23:32:44 +08:00
// DataValidationErrorStyle defined the style of data validation error alert.
2018-07-30 22:09:41 +08:00
type DataValidationErrorStyle int
2018-09-01 23:32:44 +08:00
// Data validation error styles.
2018-07-30 22:09:41 +08:00
const (
_ DataValidationErrorStyle = iota
DataValidationErrorStyleStop
DataValidationErrorStyleWarning
DataValidationErrorStyleInformation
2018-07-30 22:09:41 +08:00
)
2018-09-01 23:32:44 +08:00
// Data validation error styles.
2018-07-30 22:09:41 +08:00
const (
styleStop = "stop"
styleWarning = "warning"
styleInformation = "information"
)
2018-09-01 23:32:44 +08:00
// DataValidationOperator operator enum.
2018-07-30 22:09:41 +08:00
type DataValidationOperator int
2018-09-01 23:32:44 +08:00
// Data validation operators.
2018-07-30 22:09:41 +08:00
const (
_DataValidationOperator = iota
DataValidationOperatorBetween
DataValidationOperatorEqual
DataValidationOperatorGreaterThan
DataValidationOperatorGreaterThanOrEqual
DataValidationOperatorLessThan
DataValidationOperatorLessThanOrEqual
DataValidationOperatorNotBetween
DataValidationOperatorNotEqual
)
// formulaEscaper mimics the Excel escaping rules for data validation,
// which converts `"` to `""` instead of `"`.
var formulaEscaper = strings.NewReplacer(
`&`, `&`,
`<`, `&lt;`,
`>`, `&gt;`,
`"`, `""`,
)
2018-09-01 23:32:44 +08:00
// NewDataValidation return data validation struct.
2018-07-30 22:09:41 +08:00
func NewDataValidation(allowBlank bool) *DataValidation {
return &DataValidation{
2018-09-01 23:32:44 +08:00
AllowBlank: allowBlank,
ShowErrorMessage: false,
ShowInputMessage: false,
2018-07-30 22:09:41 +08:00
}
}
2018-09-01 23:32:44 +08:00
// SetError set error notice.
func (dd *DataValidation) SetError(style DataValidationErrorStyle, title, msg string) {
dd.Error = &msg
dd.ErrorTitle = &title
2018-07-30 22:09:41 +08:00
strStyle := styleStop
switch style {
case DataValidationErrorStyleStop:
2018-07-30 22:09:41 +08:00
strStyle = styleStop
case DataValidationErrorStyleWarning:
2018-07-30 22:09:41 +08:00
strStyle = styleWarning
case DataValidationErrorStyleInformation:
2018-07-30 22:09:41 +08:00
strStyle = styleInformation
}
2018-09-01 23:32:44 +08:00
dd.ShowErrorMessage = true
2018-07-30 22:09:41 +08:00
dd.ErrorStyle = &strStyle
}
2018-09-01 23:32:44 +08:00
// SetInput set prompt notice.
func (dd *DataValidation) SetInput(title, msg string) {
2018-09-01 23:32:44 +08:00
dd.ShowInputMessage = true
dd.PromptTitle = &title
dd.Prompt = &msg
2018-07-30 22:09:41 +08:00
}
2018-09-01 23:32:44 +08:00
// SetDropList data validation list.
2018-07-30 22:09:41 +08:00
func (dd *DataValidation) SetDropList(keys []string) error {
formula := strings.Join(keys, ",")
if dataValidationFormulaStrLen < len(utf16.Encode([]rune(formula))) {
return ErrDataValidationFormulaLenth
}
dd.Formula1 = fmt.Sprintf(`<formula1>"%s"</formula1>`, formulaEscaper.Replace(formula))
2018-07-30 22:09:41 +08:00
dd.Type = convDataValidationType(typeList)
return nil
}
2018-09-01 23:32:44 +08:00
// SetRange provides function to set data validation range in drop list.
func (dd *DataValidation) SetRange(f1, f2 float64, t DataValidationType, o DataValidationOperator) error {
if math.Abs(f1) > math.MaxFloat32 || math.Abs(f2) > math.MaxFloat32 {
return ErrDataValidationRange
2018-07-30 22:09:41 +08:00
}
dd.Formula1 = fmt.Sprintf("<formula1>%.17g</formula1>", f1)
dd.Formula2 = fmt.Sprintf("<formula2>%.17g</formula2>", f2)
2018-07-30 22:09:41 +08:00
dd.Type = convDataValidationType(t)
dd.Operator = convDataValidationOperatior(o)
return nil
}
// SetSqrefDropList provides set data validation on a range with source
// reference range of the worksheet by given data validation object and
// worksheet name. The data validation object can be created by
// NewDataValidation function. For example, set data validation on
// Sheet1!A7:B8 with validation criteria source Sheet1!E1:E3 settings, create
// in-cell dropdown by allowing list source:
//
// dvRange := excelize.NewDataValidation(true)
// dvRange.Sqref = "A7:B8"
// dvRange.SetSqrefDropList("$E$1:$E$3", true)
2019-04-20 14:57:50 +08:00
// f.AddDataValidation("Sheet1", dvRange)
//
func (dd *DataValidation) SetSqrefDropList(sqref string, isCurrentSheet bool) error {
if isCurrentSheet {
dd.Formula1 = sqref
dd.Type = convDataValidationType(typeList)
return nil
}
2018-09-12 15:47:56 +08:00
return fmt.Errorf("cross-sheet sqref cell are not supported")
}
2018-09-01 23:32:44 +08:00
// SetSqref provides function to set data validation range in drop list.
2018-07-30 22:09:41 +08:00
func (dd *DataValidation) SetSqref(sqref string) {
if dd.Sqref == "" {
dd.Sqref = sqref
} else {
dd.Sqref = fmt.Sprintf("%s %s", dd.Sqref, sqref)
}
}
2018-09-01 23:32:44 +08:00
// convDataValidationType get excel data validation type.
2018-07-30 22:09:41 +08:00
func convDataValidationType(t DataValidationType) string {
typeMap := map[DataValidationType]string{
typeNone: "none",
DataValidationTypeCustom: "custom",
DataValidationTypeDate: "date",
DataValidationTypeDecimal: "decimal",
typeList: "list",
DataValidationTypeTextLeng: "textLength",
DataValidationTypeTime: "time",
DataValidationTypeWhole: "whole",
}
return typeMap[t]
}
2018-09-01 23:32:44 +08:00
// convDataValidationOperatior get excel data validation operator.
2018-07-30 22:09:41 +08:00
func convDataValidationOperatior(o DataValidationOperator) string {
typeMap := map[DataValidationOperator]string{
DataValidationOperatorBetween: "between",
DataValidationOperatorEqual: "equal",
DataValidationOperatorGreaterThan: "greaterThan",
DataValidationOperatorGreaterThanOrEqual: "greaterThanOrEqual",
DataValidationOperatorLessThan: "lessThan",
DataValidationOperatorLessThanOrEqual: "lessThanOrEqual",
DataValidationOperatorNotBetween: "notBetween",
DataValidationOperatorNotEqual: "notEqual",
}
return typeMap[o]
}
2018-09-01 23:32:44 +08:00
// AddDataValidation provides set data validation on a range of the worksheet
// by given data validation object and worksheet name. The data validation
// object can be created by NewDataValidation function.
//
// Example 1, set data validation on Sheet1!A1:B2 with validation criteria
2018-09-02 01:44:32 +08:00
// settings, show error alert after invalid data is entered with "Stop" style
2018-09-01 23:32:44 +08:00
// and custom title "error body":
//
// dvRange := excelize.NewDataValidation(true)
2018-09-02 01:44:32 +08:00
// dvRange.Sqref = "A1:B2"
2018-09-01 23:32:44 +08:00
// dvRange.SetRange(10, 20, excelize.DataValidationTypeWhole, excelize.DataValidationOperatorBetween)
// dvRange.SetError(excelize.DataValidationErrorStyleStop, "error title", "error body")
2019-04-20 14:57:50 +08:00
// err := f.AddDataValidation("Sheet1", dvRange)
2018-09-01 23:32:44 +08:00
//
// Example 2, set data validation on Sheet1!A3:B4 with validation criteria
// settings, and show input message when cell is selected:
//
// dvRange = excelize.NewDataValidation(true)
// dvRange.Sqref = "A3:B4"
// dvRange.SetRange(10, 20, excelize.DataValidationTypeWhole, excelize.DataValidationOperatorGreaterThan)
// dvRange.SetInput("input title", "input body")
2019-04-20 14:57:50 +08:00
// err = f.AddDataValidation("Sheet1", dvRange)
2018-09-01 23:32:44 +08:00
//
2018-09-02 01:44:32 +08:00
// Example 3, set data validation on Sheet1!A5:B6 with validation criteria
// settings, create in-cell dropdown by allowing list source:
2018-09-01 23:32:44 +08:00
//
// dvRange = excelize.NewDataValidation(true)
// dvRange.Sqref = "A5:B6"
// dvRange.SetDropList([]string{"1", "2", "3"})
2019-04-20 14:57:50 +08:00
// err = f.AddDataValidation("Sheet1", dvRange)
2018-09-01 23:32:44 +08:00
//
func (f *File) AddDataValidation(sheet string, dv *DataValidation) error {
ws, err := f.workSheetReader(sheet)
if err != nil {
return err
}
if nil == ws.DataValidations {
ws.DataValidations = new(xlsxDataValidations)
2018-07-30 22:09:41 +08:00
}
ws.DataValidations.DataValidation = append(ws.DataValidations.DataValidation, dv)
ws.DataValidations.Count = len(ws.DataValidations.DataValidation)
return err
2018-07-30 22:09:41 +08:00
}
// DeleteDataValidation delete data validation by given worksheet name and
// reference sequence.
func (f *File) DeleteDataValidation(sheet, sqref string) error {
ws, err := f.workSheetReader(sheet)
if err != nil {
return err
}
if ws.DataValidations == nil {
return nil
}
dv := ws.DataValidations
for i := 0; i < len(dv.DataValidation); i++ {
if dv.DataValidation[i].Sqref == sqref {
dv.DataValidation = append(dv.DataValidation[:i], dv.DataValidation[i+1:]...)
i--
}
}
dv.Count = len(dv.DataValidation)
if dv.Count == 0 {
ws.DataValidations = nil
}
return nil
}