datavalidation.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. // Copyright 2016 - 2023 The excelize Authors. All rights reserved. Use of
  2. // this source code is governed by a BSD-style license that can be found in
  3. // the LICENSE file.
  4. //
  5. // Package excelize providing a set of functions that allow you to write to and
  6. // read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and
  7. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
  8. // Supports complex components by high compatibility, and provided streaming
  9. // API for generating or reading data from a worksheet with huge amounts of
  10. // data. This library needs Go version 1.16 or later.
  11. package excelize
  12. import (
  13. "fmt"
  14. "math"
  15. "strings"
  16. "unicode/utf16"
  17. )
  18. // DataValidationType defined the type of data validation.
  19. type DataValidationType int
  20. // Data validation types.
  21. const (
  22. _DataValidationType = iota
  23. typeNone // inline use
  24. DataValidationTypeCustom
  25. DataValidationTypeDate
  26. DataValidationTypeDecimal
  27. typeList // inline use
  28. DataValidationTypeTextLength
  29. DataValidationTypeTime
  30. // DataValidationTypeWhole Integer
  31. DataValidationTypeWhole
  32. )
  33. // DataValidationErrorStyle defined the style of data validation error alert.
  34. type DataValidationErrorStyle int
  35. // Data validation error styles.
  36. const (
  37. _ DataValidationErrorStyle = iota
  38. DataValidationErrorStyleStop
  39. DataValidationErrorStyleWarning
  40. DataValidationErrorStyleInformation
  41. )
  42. // Data validation error styles.
  43. const (
  44. styleStop = "stop"
  45. styleWarning = "warning"
  46. styleInformation = "information"
  47. )
  48. // DataValidationOperator operator enum.
  49. type DataValidationOperator int
  50. // Data validation operators.
  51. const (
  52. _DataValidationOperator = iota
  53. DataValidationOperatorBetween
  54. DataValidationOperatorEqual
  55. DataValidationOperatorGreaterThan
  56. DataValidationOperatorGreaterThanOrEqual
  57. DataValidationOperatorLessThan
  58. DataValidationOperatorLessThanOrEqual
  59. DataValidationOperatorNotBetween
  60. DataValidationOperatorNotEqual
  61. )
  62. // formulaEscaper mimics the Excel escaping rules for data validation,
  63. // which converts `"` to `""` instead of `"`.
  64. var formulaEscaper = strings.NewReplacer(
  65. `&`, `&`,
  66. `<`, `&lt;`,
  67. `>`, `&gt;`,
  68. `"`, `""`,
  69. )
  70. // NewDataValidation return data validation struct.
  71. func NewDataValidation(allowBlank bool) *DataValidation {
  72. return &DataValidation{
  73. AllowBlank: allowBlank,
  74. ShowErrorMessage: false,
  75. ShowInputMessage: false,
  76. }
  77. }
  78. // SetError set error notice.
  79. func (dv *DataValidation) SetError(style DataValidationErrorStyle, title, msg string) {
  80. dv.Error = &msg
  81. dv.ErrorTitle = &title
  82. strStyle := styleStop
  83. switch style {
  84. case DataValidationErrorStyleStop:
  85. strStyle = styleStop
  86. case DataValidationErrorStyleWarning:
  87. strStyle = styleWarning
  88. case DataValidationErrorStyleInformation:
  89. strStyle = styleInformation
  90. }
  91. dv.ShowErrorMessage = true
  92. dv.ErrorStyle = &strStyle
  93. }
  94. // SetInput set prompt notice.
  95. func (dv *DataValidation) SetInput(title, msg string) {
  96. dv.ShowInputMessage = true
  97. dv.PromptTitle = &title
  98. dv.Prompt = &msg
  99. }
  100. // SetDropList data validation list.
  101. func (dv *DataValidation) SetDropList(keys []string) error {
  102. formula := strings.Join(keys, ",")
  103. if MaxFieldLength < len(utf16.Encode([]rune(formula))) {
  104. return ErrDataValidationFormulaLength
  105. }
  106. dv.Formula1 = fmt.Sprintf(`<formula1>"%s"</formula1>`, formulaEscaper.Replace(formula))
  107. dv.Type = convDataValidationType(typeList)
  108. return nil
  109. }
  110. // SetRange provides function to set data validation range in drop list, only
  111. // accepts int, float64, or string data type formula argument.
  112. func (dv *DataValidation) SetRange(f1, f2 interface{}, t DataValidationType, o DataValidationOperator) error {
  113. var formula1, formula2 string
  114. switch v := f1.(type) {
  115. case int:
  116. formula1 = fmt.Sprintf("<formula1>%d</formula1>", v)
  117. case float64:
  118. if math.Abs(v) > math.MaxFloat32 {
  119. return ErrDataValidationRange
  120. }
  121. formula1 = fmt.Sprintf("<formula1>%.17g</formula1>", v)
  122. case string:
  123. formula1 = fmt.Sprintf("<formula1>%s</formula1>", v)
  124. default:
  125. return ErrParameterInvalid
  126. }
  127. switch v := f2.(type) {
  128. case int:
  129. formula2 = fmt.Sprintf("<formula2>%d</formula2>", v)
  130. case float64:
  131. if math.Abs(v) > math.MaxFloat32 {
  132. return ErrDataValidationRange
  133. }
  134. formula2 = fmt.Sprintf("<formula2>%.17g</formula2>", v)
  135. case string:
  136. formula2 = fmt.Sprintf("<formula2>%s</formula2>", v)
  137. default:
  138. return ErrParameterInvalid
  139. }
  140. dv.Formula1, dv.Formula2 = formula1, formula2
  141. dv.Type = convDataValidationType(t)
  142. dv.Operator = convDataValidationOperator(o)
  143. return nil
  144. }
  145. // SetSqrefDropList provides set data validation on a range with source
  146. // reference range of the worksheet by given data validation object and
  147. // worksheet name. The data validation object can be created by
  148. // NewDataValidation function. For example, set data validation on
  149. // Sheet1!A7:B8 with validation criteria source Sheet1!E1:E3 settings, create
  150. // in-cell dropdown by allowing list source:
  151. //
  152. // dv := excelize.NewDataValidation(true)
  153. // dv.Sqref = "A7:B8"
  154. // dv.SetSqrefDropList("$E$1:$E$3")
  155. // err := f.AddDataValidation("Sheet1", dv)
  156. func (dv *DataValidation) SetSqrefDropList(sqref string) {
  157. dv.Formula1 = fmt.Sprintf("<formula1>%s</formula1>", sqref)
  158. dv.Type = convDataValidationType(typeList)
  159. }
  160. // SetSqref provides function to set data validation range in drop list.
  161. func (dv *DataValidation) SetSqref(sqref string) {
  162. if dv.Sqref == "" {
  163. dv.Sqref = sqref
  164. } else {
  165. dv.Sqref = fmt.Sprintf("%s %s", dv.Sqref, sqref)
  166. }
  167. }
  168. // convDataValidationType get excel data validation type.
  169. func convDataValidationType(t DataValidationType) string {
  170. typeMap := map[DataValidationType]string{
  171. typeNone: "none",
  172. DataValidationTypeCustom: "custom",
  173. DataValidationTypeDate: "date",
  174. DataValidationTypeDecimal: "decimal",
  175. typeList: "list",
  176. DataValidationTypeTextLength: "textLength",
  177. DataValidationTypeTime: "time",
  178. DataValidationTypeWhole: "whole",
  179. }
  180. return typeMap[t]
  181. }
  182. // convDataValidationOperator get excel data validation operator.
  183. func convDataValidationOperator(o DataValidationOperator) string {
  184. typeMap := map[DataValidationOperator]string{
  185. DataValidationOperatorBetween: "between",
  186. DataValidationOperatorEqual: "equal",
  187. DataValidationOperatorGreaterThan: "greaterThan",
  188. DataValidationOperatorGreaterThanOrEqual: "greaterThanOrEqual",
  189. DataValidationOperatorLessThan: "lessThan",
  190. DataValidationOperatorLessThanOrEqual: "lessThanOrEqual",
  191. DataValidationOperatorNotBetween: "notBetween",
  192. DataValidationOperatorNotEqual: "notEqual",
  193. }
  194. return typeMap[o]
  195. }
  196. // AddDataValidation provides set data validation on a range of the worksheet
  197. // by given data validation object and worksheet name. The data validation
  198. // object can be created by NewDataValidation function.
  199. //
  200. // Example 1, set data validation on Sheet1!A1:B2 with validation criteria
  201. // settings, show error alert after invalid data is entered with "Stop" style
  202. // and custom title "error body":
  203. //
  204. // dv := excelize.NewDataValidation(true)
  205. // dv.Sqref = "A1:B2"
  206. // dv.SetRange(10, 20, excelize.DataValidationTypeWhole, excelize.DataValidationOperatorBetween)
  207. // dv.SetError(excelize.DataValidationErrorStyleStop, "error title", "error body")
  208. // err := f.AddDataValidation("Sheet1", dv)
  209. //
  210. // Example 2, set data validation on Sheet1!A3:B4 with validation criteria
  211. // settings, and show input message when cell is selected:
  212. //
  213. // dv = excelize.NewDataValidation(true)
  214. // dv.Sqref = "A3:B4"
  215. // dv.SetRange(10, 20, excelize.DataValidationTypeWhole, excelize.DataValidationOperatorGreaterThan)
  216. // dv.SetInput("input title", "input body")
  217. // err = f.AddDataValidation("Sheet1", dv)
  218. //
  219. // Example 3, set data validation on Sheet1!A5:B6 with validation criteria
  220. // settings, create in-cell dropdown by allowing list source:
  221. //
  222. // dv = excelize.NewDataValidation(true)
  223. // dv.Sqref = "A5:B6"
  224. // dv.SetDropList([]string{"1", "2", "3"})
  225. // err = f.AddDataValidation("Sheet1", dv)
  226. func (f *File) AddDataValidation(sheet string, dv *DataValidation) error {
  227. ws, err := f.workSheetReader(sheet)
  228. if err != nil {
  229. return err
  230. }
  231. if nil == ws.DataValidations {
  232. ws.DataValidations = new(xlsxDataValidations)
  233. }
  234. ws.DataValidations.DataValidation = append(ws.DataValidations.DataValidation, dv)
  235. ws.DataValidations.Count = len(ws.DataValidations.DataValidation)
  236. return err
  237. }
  238. // GetDataValidations returns data validations list by given worksheet name.
  239. func (f *File) GetDataValidations(sheet string) ([]*DataValidation, error) {
  240. ws, err := f.workSheetReader(sheet)
  241. if err != nil {
  242. return nil, err
  243. }
  244. if ws.DataValidations == nil || len(ws.DataValidations.DataValidation) == 0 {
  245. return nil, err
  246. }
  247. return ws.DataValidations.DataValidation, err
  248. }
  249. // DeleteDataValidation delete data validation by given worksheet name and
  250. // reference sequence. All data validations in the worksheet will be deleted
  251. // if not specify reference sequence parameter.
  252. func (f *File) DeleteDataValidation(sheet string, sqref ...string) error {
  253. ws, err := f.workSheetReader(sheet)
  254. if err != nil {
  255. return err
  256. }
  257. if ws.DataValidations == nil {
  258. return nil
  259. }
  260. if sqref == nil {
  261. ws.DataValidations = nil
  262. return nil
  263. }
  264. delCells, err := f.flatSqref(sqref[0])
  265. if err != nil {
  266. return err
  267. }
  268. dv := ws.DataValidations
  269. for i := 0; i < len(dv.DataValidation); i++ {
  270. var applySqref []string
  271. colCells, err := f.flatSqref(dv.DataValidation[i].Sqref)
  272. if err != nil {
  273. return err
  274. }
  275. for col, cells := range delCells {
  276. for _, cell := range cells {
  277. idx := inCoordinates(colCells[col], cell)
  278. if idx != -1 {
  279. colCells[col] = append(colCells[col][:idx], colCells[col][idx+1:]...)
  280. }
  281. }
  282. }
  283. for _, col := range colCells {
  284. applySqref = append(applySqref, f.squashSqref(col)...)
  285. }
  286. dv.DataValidation[i].Sqref = strings.Join(applySqref, " ")
  287. if len(applySqref) == 0 {
  288. dv.DataValidation = append(dv.DataValidation[:i], dv.DataValidation[i+1:]...)
  289. i--
  290. }
  291. }
  292. dv.Count = len(dv.DataValidation)
  293. if dv.Count == 0 {
  294. ws.DataValidations = nil
  295. }
  296. return nil
  297. }
  298. // squashSqref generates cell reference sequence by given cells coordinates list.
  299. func (f *File) squashSqref(cells [][]int) []string {
  300. if len(cells) == 1 {
  301. cell, _ := CoordinatesToCellName(cells[0][0], cells[0][1])
  302. return []string{cell}
  303. } else if len(cells) == 0 {
  304. return []string{}
  305. }
  306. var res []string
  307. l, r := 0, 0
  308. for i := 1; i < len(cells); i++ {
  309. if cells[i][0] == cells[r][0] && cells[i][1]-cells[r][1] > 1 {
  310. curr, _ := f.coordinatesToRangeRef(append(cells[l], cells[r]...))
  311. if l == r {
  312. curr, _ = CoordinatesToCellName(cells[l][0], cells[l][1])
  313. }
  314. res = append(res, curr)
  315. l, r = i, i
  316. } else {
  317. r++
  318. }
  319. }
  320. curr, _ := f.coordinatesToRangeRef(append(cells[l], cells[r]...))
  321. if l == r {
  322. curr, _ = CoordinatesToCellName(cells[l][0], cells[l][1])
  323. }
  324. return append(res, curr)
  325. }