From 054885219000f5c94c59fdf989e7c2110a023164 Mon Sep 17 00:00:00 2001 From: rentiansheng Date: Mon, 30 Jul 2018 22:09:41 +0800 Subject: [PATCH 1/4] data validation funcation --- datavalidation.go | 196 ++++++++++++++++++++++++++++++++++++++++++++++ xmlWorksheet.go | 26 ++++-- 2 files changed, 217 insertions(+), 5 deletions(-) create mode 100644 datavalidation.go diff --git a/datavalidation.go b/datavalidation.go new file mode 100644 index 0000000..5dae7c9 --- /dev/null +++ b/datavalidation.go @@ -0,0 +1,196 @@ +package excelize + +import ( + "fmt" + "strings" +) + +type DataValidationType int + +// Data validation types +const ( + _DataValidationType = iota + typeNone //inline use + DataValidationTypeCustom + DataValidationTypeDate + DataValidationTypeDecimal + typeList //inline use + DataValidationTypeTextLeng + DataValidationTypeTime + // DataValidationTypeWhole Integer + DataValidationTypeWhole +) + +const ( + // dataValidationFormulaStrLen 255 characters+ 2 quotes + dataValidationFormulaStrLen = 257 + // dataValidationFormulaStrLenErr + dataValidationFormulaStrLenErr = "data validation must be 0-255 characters" +) + +type DataValidationErrorStyle int + +// Data validation error styles +const ( + _ DataValidationErrorStyle = iota + DataValidationStyleStop + DataValidationStyleWarning + DataValidationStyleInformation +) + +// Data validation error styles +const ( + styleStop = "stop" + styleWarning = "warning" + styleInformation = "information" +) + +// DataValidationOperator operator enum +type DataValidationOperator int + +// Data validation operators +const ( + _DataValidationOperator = iota + DataValidationOperatorBetween + DataValidationOperatorEqual + DataValidationOperatorGreaterThan + DataValidationOperatorGreaterThanOrEqual + DataValidationOperatorLessThan + DataValidationOperatorLessThanOrEqual + DataValidationOperatorNotBetween + DataValidationOperatorNotEqual +) + +// NewDataValidation return data validation struct +func NewDataValidation(allowBlank bool) *DataValidation { + return &DataValidation{ + AllowBlank: convBoolToStr(allowBlank), + ShowErrorMessage: convBoolToStr(false), + ShowInputMessage: convBoolToStr(false), + } +} + +// SetError set error notice +func (dd *DataValidation) SetError(style DataValidationErrorStyle, title, msg *string) { + dd.Error = msg + dd.ErrorTitle = title + strStyle := styleStop + switch style { + case DataValidationStyleStop: + strStyle = styleStop + case DataValidationStyleWarning: + strStyle = styleWarning + case DataValidationStyleInformation: + strStyle = styleInformation + + } + dd.ShowErrorMessage = convBoolToStr(true) + dd.ErrorStyle = &strStyle +} + +// SetInput set prompt notice +func (dd *DataValidation) SetInput(title, msg *string) { + dd.ShowInputMessage = convBoolToStr(true) + dd.PromptTitle = title + dd.Prompt = msg +} + +// SetDropList data validation list +func (dd *DataValidation) SetDropList(keys []string) error { + dd.Formula1 = "\"" + strings.Join(keys, ",") + "\"" + dd.Type = convDataValidationType(typeList) + return nil +} + +// SetDropList data validation range +func (dd *DataValidation) SetRange(f1, f2 int, t DataValidationType, o DataValidationOperator) error { + formula1 := fmt.Sprintf("%d", f1) + formula2 := fmt.Sprintf("%d", f2) + if dataValidationFormulaStrLen < len(dd.Formula1) || dataValidationFormulaStrLen < len(dd.Formula2) { + return fmt.Errorf(dataValidationFormulaStrLenErr) + } + switch o { + case DataValidationOperatorBetween: + if f1 > f2 { + tmp := formula1 + formula1 = formula2 + formula2 = tmp + } + case DataValidationOperatorNotBetween: + if f1 > f2 { + tmp := formula1 + formula1 = formula2 + formula2 = tmp + } + } + + dd.Formula1 = formula1 + dd.Formula2 = formula2 + dd.Type = convDataValidationType(t) + dd.Operator = convDataValidationOperatior(o) + return nil +} + +// SetDropList data validation range +func (dd *DataValidation) SetSqref(sqref string) { + if dd.Sqref == "" { + dd.Sqref = sqref + } else { + dd.Sqref = fmt.Sprintf("%s %s", dd.Sqref, sqref) + } +} + +// convBoolToStr convert boolean to string , false to 0, true to 1 +func convBoolToStr(bl bool) string { + if bl { + return "1" + } + return "0" +} + +// convDataValidationType get excel data validation type +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] + +} + +// convDataValidationOperatior get excel data validation operator +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] + +} + +func (f *File) AddDataValidation(sheet string, dv *DataValidation) { + xlsx := f.workSheetReader(sheet) + if nil == xlsx.DataValidations { + xlsx.DataValidations = new(xlsxDataValidations) + } + xlsx.DataValidations.DataValidation = append(xlsx.DataValidations.DataValidation, dv) + xlsx.DataValidations.Count = len(xlsx.DataValidations.DataValidation) +} + +func (f *File) GetDataValidation(sheet, sqref string) { + +} diff --git a/xmlWorksheet.go b/xmlWorksheet.go index 87d66a1..f2ac9fb 100644 --- a/xmlWorksheet.go +++ b/xmlWorksheet.go @@ -294,11 +294,27 @@ type xlsxMergeCells struct { // xlsxDataValidations expresses all data validation information for cells in a // sheet which have data validation features applied. type xlsxDataValidations struct { - Count int `xml:"count,attr,omitempty"` - DisablePrompts bool `xml:"disablePrompts,attr,omitempty"` - XWindow int `xml:"xWindow,attr,omitempty"` - YWindow int `xml:"yWindow,attr,omitempty"` - DataValidation string `xml:",innerxml"` + Count int `xml:"count,attr,omitempty"` + DisablePrompts bool `xml:"disablePrompts,attr,omitempty"` + XWindow int `xml:"xWindow,attr,omitempty"` + YWindow int `xml:"yWindow,attr,omitempty"` + DataValidation []*DataValidation `xml:"dataValidation,innerxml"` +} + +type DataValidation struct { + AllowBlank string `xml:"allowBlank,attr"` // allow empty + ShowInputMessage string `xml:"showInputMessage,attr"` // 1, true,0,false, select cell, Whether the input message is displayed + ShowErrorMessage string `xml:"showErrorMessage,attr"` // 1, true,0,false, input error value, Whether the error message is displayed + ErrorStyle *string `xml:"errorStyle,attr"` //error icon style, warning, infomation,stop + ErrorTitle *string `xml:"errorTitle,attr"` // error title + Operator string `xml:"operator,attr"` // + Error *string `xml:"error,attr"` // input error value, notice message + PromptTitle *string `xml:"promptTitle"` + Prompt *string `xml:"prompt,attr"` + Type string `xml:"type,attr"` //data type, none,custom,date,decimal,list, textLength,time,whole + Sqref string `xml:"sqref,attr"` //Validity of data validation rules, cell and range, eg: A1 OR A1:A20 + Formula1 string `xml:"formula1"` // data validation role + Formula2 string `xml:"formula2"` //data validation role } // xlsxC directly maps the c element in the namespace From 24a8d64f939afb5c15b04e552b3d3b7046daa851 Mon Sep 17 00:00:00 2001 From: rentiansheng Date: Sat, 1 Sep 2018 19:38:30 +0800 Subject: [PATCH 2/4] add datavalidation test and fixed struct bug issue #240 --- datavalidation.go | 32 ++++++++++++++------------------ datavalidation_test.go | 32 ++++++++++++++++++++++++++++++++ xmlWorksheet.go | 4 ++-- 3 files changed, 48 insertions(+), 20 deletions(-) create mode 100644 datavalidation_test.go diff --git a/datavalidation.go b/datavalidation.go index 5dae7c9..f1db732 100644 --- a/datavalidation.go +++ b/datavalidation.go @@ -33,9 +33,9 @@ type DataValidationErrorStyle int // Data validation error styles const ( _ DataValidationErrorStyle = iota - DataValidationStyleStop - DataValidationStyleWarning - DataValidationStyleInformation + DataValidationErrorStyleStop + DataValidationErrorStyleWarning + DataValidationErrorStyleInformation ) // Data validation error styles @@ -71,16 +71,16 @@ func NewDataValidation(allowBlank bool) *DataValidation { } // SetError set error notice -func (dd *DataValidation) SetError(style DataValidationErrorStyle, title, msg *string) { - dd.Error = msg - dd.ErrorTitle = title +func (dd *DataValidation) SetError(style DataValidationErrorStyle, title, msg string) { + dd.Error = &msg + dd.ErrorTitle = &title strStyle := styleStop switch style { - case DataValidationStyleStop: + case DataValidationErrorStyleStop: strStyle = styleStop - case DataValidationStyleWarning: + case DataValidationErrorStyleWarning: strStyle = styleWarning - case DataValidationStyleInformation: + case DataValidationErrorStyleInformation: strStyle = styleInformation } @@ -89,10 +89,10 @@ func (dd *DataValidation) SetError(style DataValidationErrorStyle, title, msg *s } // SetInput set prompt notice -func (dd *DataValidation) SetInput(title, msg *string) { +func (dd *DataValidation) SetInput(title, msg string) { dd.ShowInputMessage = convBoolToStr(true) - dd.PromptTitle = title - dd.Prompt = msg + dd.PromptTitle = &title + dd.Prompt = &msg } // SetDropList data validation list @@ -109,7 +109,7 @@ func (dd *DataValidation) SetRange(f1, f2 int, t DataValidationType, o DataValid if dataValidationFormulaStrLen < len(dd.Formula1) || dataValidationFormulaStrLen < len(dd.Formula2) { return fmt.Errorf(dataValidationFormulaStrLenErr) } - switch o { + /*switch o { case DataValidationOperatorBetween: if f1 > f2 { tmp := formula1 @@ -122,7 +122,7 @@ func (dd *DataValidation) SetRange(f1, f2 int, t DataValidationType, o DataValid formula1 = formula2 formula2 = tmp } - } + }*/ dd.Formula1 = formula1 dd.Formula2 = formula2 @@ -190,7 +190,3 @@ func (f *File) AddDataValidation(sheet string, dv *DataValidation) { xlsx.DataValidations.DataValidation = append(xlsx.DataValidations.DataValidation, dv) xlsx.DataValidations.Count = len(xlsx.DataValidations.DataValidation) } - -func (f *File) GetDataValidation(sheet, sqref string) { - -} diff --git a/datavalidation_test.go b/datavalidation_test.go new file mode 100644 index 0000000..718131f --- /dev/null +++ b/datavalidation_test.go @@ -0,0 +1,32 @@ +package excelize + +import ( + "testing" +) + +func TestDataValidation(t *testing.T) { + xlsx := NewFile() + + dvRange := NewDataValidation(true) + dvRange.Sqref = "A1:B2" + dvRange.SetRange(10, 20, DataValidationTypeWhole, DataValidationOperatorBetween) + dvRange.SetError(DataValidationErrorStyleStop, "error title", "error body") + xlsx.AddDataValidation("Sheet1", dvRange) + + dvRange = NewDataValidation(true) + dvRange.Sqref = "A3:B4" + dvRange.SetRange(10, 20, DataValidationTypeWhole, DataValidationOperatorGreaterThan) + dvRange.SetInput("input title", "input body") + xlsx.AddDataValidation("Sheet1", dvRange) + + dvRange = NewDataValidation(true) + dvRange.Sqref = "A5:B6" + dvRange.SetDropList([]string{"1", "2", "3"}) + xlsx.AddDataValidation("Sheet1", dvRange) + + // Test write file to given path. + err := xlsx.SaveAs("./test/Bookdatavalition.xlsx") + if err != nil { + t.Error(err) + } +} diff --git a/xmlWorksheet.go b/xmlWorksheet.go index f2ac9fb..25e3904 100644 --- a/xmlWorksheet.go +++ b/xmlWorksheet.go @@ -18,7 +18,7 @@ type xlsxWorksheet struct { MergeCells *xlsxMergeCells `xml:"mergeCells"` PhoneticPr *xlsxPhoneticPr `xml:"phoneticPr"` ConditionalFormatting []*xlsxConditionalFormatting `xml:"conditionalFormatting"` - DataValidations *xlsxDataValidations `xml:"dataValidations"` + DataValidations *xlsxDataValidations `xml:"dataValidations,omitempty"` Hyperlinks *xlsxHyperlinks `xml:"hyperlinks"` PrintOptions *xlsxPrintOptions `xml:"printOptions"` PageMargins *xlsxPageMargins `xml:"pageMargins"` @@ -298,7 +298,7 @@ type xlsxDataValidations struct { DisablePrompts bool `xml:"disablePrompts,attr,omitempty"` XWindow int `xml:"xWindow,attr,omitempty"` YWindow int `xml:"yWindow,attr,omitempty"` - DataValidation []*DataValidation `xml:"dataValidation,innerxml"` + DataValidation []*DataValidation `xml:"dataValidation"` } type DataValidation struct { From 562ba3d234489796b94aca01eda88aea7b0c5cbf Mon Sep 17 00:00:00 2001 From: xuri Date: Sat, 1 Sep 2018 23:32:44 +0800 Subject: [PATCH 3/4] Add function doc and fix golint error --- datavalidation.go | 95 ++++++++++++++++++++++++++--------------------- 1 file changed, 53 insertions(+), 42 deletions(-) diff --git a/datavalidation.go b/datavalidation.go index f1db732..62d2cf8 100644 --- a/datavalidation.go +++ b/datavalidation.go @@ -5,16 +5,17 @@ import ( "strings" ) +// DataValidationType defined the type of data validation. type DataValidationType int -// Data validation types +// Data validation types. const ( _DataValidationType = iota - typeNone //inline use + typeNone // inline use DataValidationTypeCustom DataValidationTypeDate DataValidationTypeDecimal - typeList //inline use + typeList // inline use DataValidationTypeTextLeng DataValidationTypeTime // DataValidationTypeWhole Integer @@ -28,9 +29,10 @@ const ( dataValidationFormulaStrLenErr = "data validation must be 0-255 characters" ) +// DataValidationErrorStyle defined the style of data validation error alert. type DataValidationErrorStyle int -// Data validation error styles +// Data validation error styles. const ( _ DataValidationErrorStyle = iota DataValidationErrorStyleStop @@ -38,17 +40,17 @@ const ( DataValidationErrorStyleInformation ) -// Data validation error styles +// Data validation error styles. const ( styleStop = "stop" styleWarning = "warning" styleInformation = "information" ) -// DataValidationOperator operator enum +// DataValidationOperator operator enum. type DataValidationOperator int -// Data validation operators +// Data validation operators. const ( _DataValidationOperator = iota DataValidationOperatorBetween @@ -61,16 +63,16 @@ const ( DataValidationOperatorNotEqual ) -// NewDataValidation return data validation struct +// NewDataValidation return data validation struct. func NewDataValidation(allowBlank bool) *DataValidation { return &DataValidation{ - AllowBlank: convBoolToStr(allowBlank), - ShowErrorMessage: convBoolToStr(false), - ShowInputMessage: convBoolToStr(false), + AllowBlank: allowBlank, + ShowErrorMessage: false, + ShowInputMessage: false, } } -// SetError set error notice +// SetError set error notice. func (dd *DataValidation) SetError(style DataValidationErrorStyle, title, msg string) { dd.Error = &msg dd.ErrorTitle = &title @@ -84,45 +86,31 @@ func (dd *DataValidation) SetError(style DataValidationErrorStyle, title, msg st strStyle = styleInformation } - dd.ShowErrorMessage = convBoolToStr(true) + dd.ShowErrorMessage = true dd.ErrorStyle = &strStyle } -// SetInput set prompt notice +// SetInput set prompt notice. func (dd *DataValidation) SetInput(title, msg string) { - dd.ShowInputMessage = convBoolToStr(true) + dd.ShowInputMessage = true dd.PromptTitle = &title dd.Prompt = &msg } -// SetDropList data validation list +// SetDropList data validation list. func (dd *DataValidation) SetDropList(keys []string) error { dd.Formula1 = "\"" + strings.Join(keys, ",") + "\"" dd.Type = convDataValidationType(typeList) return nil } -// SetDropList data validation range +// SetRange provides function to set data validation range in drop list. func (dd *DataValidation) SetRange(f1, f2 int, t DataValidationType, o DataValidationOperator) error { formula1 := fmt.Sprintf("%d", f1) formula2 := fmt.Sprintf("%d", f2) if dataValidationFormulaStrLen < len(dd.Formula1) || dataValidationFormulaStrLen < len(dd.Formula2) { return fmt.Errorf(dataValidationFormulaStrLenErr) } - /*switch o { - case DataValidationOperatorBetween: - if f1 > f2 { - tmp := formula1 - formula1 = formula2 - formula2 = tmp - } - case DataValidationOperatorNotBetween: - if f1 > f2 { - tmp := formula1 - formula1 = formula2 - formula2 = tmp - } - }*/ dd.Formula1 = formula1 dd.Formula2 = formula2 @@ -131,7 +119,7 @@ func (dd *DataValidation) SetRange(f1, f2 int, t DataValidationType, o DataValid return nil } -// SetDropList data validation range +// SetSqref provides function to set data validation range in drop list. func (dd *DataValidation) SetSqref(sqref string) { if dd.Sqref == "" { dd.Sqref = sqref @@ -140,15 +128,7 @@ func (dd *DataValidation) SetSqref(sqref string) { } } -// convBoolToStr convert boolean to string , false to 0, true to 1 -func convBoolToStr(bl bool) string { - if bl { - return "1" - } - return "0" -} - -// convDataValidationType get excel data validation type +// convDataValidationType get excel data validation type. func convDataValidationType(t DataValidationType) string { typeMap := map[DataValidationType]string{ typeNone: "none", @@ -165,7 +145,7 @@ func convDataValidationType(t DataValidationType) string { } -// convDataValidationOperatior get excel data validation operator +// convDataValidationOperatior get excel data validation operator. func convDataValidationOperatior(o DataValidationOperator) string { typeMap := map[DataValidationOperator]string{ DataValidationOperatorBetween: "between", @@ -182,6 +162,37 @@ func convDataValidationOperatior(o DataValidationOperator) string { } +// 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 +// settings, show error alert after invalid data is entered whth "Stop" style +// and custom title "error body": +// +// dvRange := excelize.NewDataValidation(true) +// dvRange.Sqref = "A1:B2" +// dvRange.SetRange(10, 20, excelize.DataValidationTypeWhole, excelize.DataValidationOperatorBetween) +// dvRange.SetError(excelize.DataValidationErrorStyleStop, "error title", "error body") +// xlsx.AddDataValidation("Sheet1", dvRange) +// +// 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") +// xlsx.AddDataValidation("Sheet1", dvRange) +// +// Example 4, set data validation on Sheet1!A5:B6 with validation criteria +// settings, create in-cell dropdown by allow list source: +// +// dvRange = excelize.NewDataValidation(true) +// dvRange.Sqref = "A5:B6" +// dvRange.SetDropList([]string{"1", "2", "3"}) +// xlsx.AddDataValidation("Sheet1", dvRange) +// func (f *File) AddDataValidation(sheet string, dv *DataValidation) { xlsx := f.workSheetReader(sheet) if nil == xlsx.DataValidations { From ba459dc659720d7504e5eb6f5bda9081a452a509 Mon Sep 17 00:00:00 2001 From: xuri Date: Sat, 1 Sep 2018 23:36:57 +0800 Subject: [PATCH 4/4] DataValidation struct changed Change `allowBlank`, `ShowErrorMessage` and `ShowInputMessage` type as boolean, add new field `ShowDropDown`, change fields orders follow as ECMA-376-1:2016 18.3.1.32. --- xmlWorksheet.go | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/xmlWorksheet.go b/xmlWorksheet.go index 25e3904..7cf4994 100644 --- a/xmlWorksheet.go +++ b/xmlWorksheet.go @@ -301,20 +301,23 @@ type xlsxDataValidations struct { DataValidation []*DataValidation `xml:"dataValidation"` } +// DataValidation directly maps the a single item of data validation defined +// on a range of the worksheet. type DataValidation struct { - AllowBlank string `xml:"allowBlank,attr"` // allow empty - ShowInputMessage string `xml:"showInputMessage,attr"` // 1, true,0,false, select cell, Whether the input message is displayed - ShowErrorMessage string `xml:"showErrorMessage,attr"` // 1, true,0,false, input error value, Whether the error message is displayed - ErrorStyle *string `xml:"errorStyle,attr"` //error icon style, warning, infomation,stop - ErrorTitle *string `xml:"errorTitle,attr"` // error title - Operator string `xml:"operator,attr"` // - Error *string `xml:"error,attr"` // input error value, notice message - PromptTitle *string `xml:"promptTitle"` + AllowBlank bool `xml:"allowBlank,attr"` + Error *string `xml:"error,attr"` + ErrorStyle *string `xml:"errorStyle,attr"` + ErrorTitle *string `xml:"errorTitle,attr"` + Operator string `xml:"operator,attr"` Prompt *string `xml:"prompt,attr"` - Type string `xml:"type,attr"` //data type, none,custom,date,decimal,list, textLength,time,whole - Sqref string `xml:"sqref,attr"` //Validity of data validation rules, cell and range, eg: A1 OR A1:A20 - Formula1 string `xml:"formula1"` // data validation role - Formula2 string `xml:"formula2"` //data validation role + PromptTitle *string `xml:"promptTitle"` + ShowDropDown bool `xml:"showDropDown,attr"` + ShowErrorMessage bool `xml:"showErrorMessage,attr"` + ShowInputMessage bool `xml:"showInputMessage,attr"` + Sqref string `xml:"sqref,attr"` + Type string `xml:"type,attr"` + Formula1 string `xml:"formula1"` + Formula2 string `xml:"formula2"` } // xlsxC directly maps the c element in the namespace