summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxuri <xuri.me@gmail.com>2021-08-06 22:44:43 +0800
committerxuri <xuri.me@gmail.com>2021-08-06 22:44:43 +0800
commitcf9fbafdd805874267a0f5d27fd1c720b148ec91 (patch)
tree4eda7bd0d974160b5f396c05cd914ba8baf504da
parent933159f9391f9be1b41b51e85885722124f8a7aa (diff)
This closes #979, fix the data validation deletion issue and tidy the internal function in the source code
-rw-r--r--adjust.go60
-rw-r--r--adjust_test.go17
-rw-r--r--datavalidation.go51
-rw-r--r--datavalidation_test.go28
-rw-r--r--lib.go108
-rw-r--r--lib_test.go21
-rw-r--r--pivotTable.go11
-rw-r--r--pivotTable_test.go4
8 files changed, 206 insertions, 94 deletions
diff --git a/adjust.go b/adjust.go
index ef7b19a..1fe6663 100644
--- a/adjust.go
+++ b/adjust.go
@@ -11,10 +11,6 @@
package excelize
-import (
- "strings"
-)
-
type adjustDirection bool
const (
@@ -194,62 +190,6 @@ func (f *File) adjustAutoFilterHelper(dir adjustDirection, coordinates []int, nu
return coordinates
}
-// areaRefToCoordinates provides a function to convert area reference to a
-// pair of coordinates.
-func (f *File) areaRefToCoordinates(ref string) ([]int, error) {
- rng := strings.Split(strings.Replace(ref, "$", "", -1), ":")
- if len(rng) < 2 {
- return nil, ErrParameterInvalid
- }
-
- return areaRangeToCoordinates(rng[0], rng[1])
-}
-
-// areaRangeToCoordinates provides a function to convert cell range to a
-// pair of coordinates.
-func areaRangeToCoordinates(firstCell, lastCell string) ([]int, error) {
- coordinates := make([]int, 4)
- var err error
- coordinates[0], coordinates[1], err = CellNameToCoordinates(firstCell)
- if err != nil {
- return coordinates, err
- }
- coordinates[2], coordinates[3], err = CellNameToCoordinates(lastCell)
- return coordinates, err
-}
-
-// sortCoordinates provides a function to correct the coordinate area, such
-// correct C1:B3 to B1:C3.
-func sortCoordinates(coordinates []int) error {
- if len(coordinates) != 4 {
- return ErrCoordinates
- }
- if coordinates[2] < coordinates[0] {
- coordinates[2], coordinates[0] = coordinates[0], coordinates[2]
- }
- if coordinates[3] < coordinates[1] {
- coordinates[3], coordinates[1] = coordinates[1], coordinates[3]
- }
- return nil
-}
-
-// coordinatesToAreaRef provides a function to convert a pair of coordinates
-// to area reference.
-func (f *File) coordinatesToAreaRef(coordinates []int) (string, error) {
- if len(coordinates) != 4 {
- return "", ErrCoordinates
- }
- firstCell, err := CoordinatesToCellName(coordinates[0], coordinates[1])
- if err != nil {
- return "", err
- }
- lastCell, err := CoordinatesToCellName(coordinates[2], coordinates[3])
- if err != nil {
- return "", err
- }
- return firstCell + ":" + lastCell, err
-}
-
// adjustMergeCells provides a function to update merged cells when inserting
// or deleting rows or columns.
func (f *File) adjustMergeCells(ws *xlsxWorksheet, dir adjustDirection, num, offset int) error {
diff --git a/adjust_test.go b/adjust_test.go
index c4af38b..ced091d 100644
--- a/adjust_test.go
+++ b/adjust_test.go
@@ -99,20 +99,3 @@ func TestAdjustCalcChain(t *testing.T) {
f.CalcChain = nil
assert.NoError(t, f.InsertCol("Sheet1", "A"))
}
-
-func TestCoordinatesToAreaRef(t *testing.T) {
- f := NewFile()
- _, err := f.coordinatesToAreaRef([]int{})
- assert.EqualError(t, err, ErrCoordinates.Error())
- _, err = f.coordinatesToAreaRef([]int{1, -1, 1, 1})
- assert.EqualError(t, err, "invalid cell coordinates [1, -1]")
- _, err = f.coordinatesToAreaRef([]int{1, 1, 1, -1})
- assert.EqualError(t, err, "invalid cell coordinates [1, -1]")
- ref, err := f.coordinatesToAreaRef([]int{1, 1, 1, 1})
- assert.NoError(t, err)
- assert.EqualValues(t, ref, "A1:A1")
-}
-
-func TestSortCoordinates(t *testing.T) {
- assert.EqualError(t, sortCoordinates(make([]int, 3)), ErrCoordinates.Error())
-}
diff --git a/datavalidation.go b/datavalidation.go
index 04dbe25..d44d2b8 100644
--- a/datavalidation.go
+++ b/datavalidation.go
@@ -258,9 +258,30 @@ func (f *File) DeleteDataValidation(sheet, sqref string) error {
if ws.DataValidations == nil {
return nil
}
+ delCells, err := f.flatSqref(sqref)
+ if err != nil {
+ return err
+ }
dv := ws.DataValidations
for i := 0; i < len(dv.DataValidation); i++ {
- if dv.DataValidation[i].Sqref == sqref {
+ applySqref := []string{}
+ colCells, err := f.flatSqref(dv.DataValidation[i].Sqref)
+ if err != nil {
+ return err
+ }
+ for col, cells := range delCells {
+ for _, cell := range cells {
+ idx := inCoordinates(colCells[col], cell)
+ if idx != -1 {
+ colCells[col] = append(colCells[col][:idx], colCells[col][idx+1:]...)
+ }
+ }
+ }
+ for _, col := range colCells {
+ applySqref = append(applySqref, f.squashSqref(col)...)
+ }
+ dv.DataValidation[i].Sqref = strings.Join(applySqref, " ")
+ if len(applySqref) == 0 {
dv.DataValidation = append(dv.DataValidation[:i], dv.DataValidation[i+1:]...)
i--
}
@@ -271,3 +292,31 @@ func (f *File) DeleteDataValidation(sheet, sqref string) error {
}
return nil
}
+
+// squashSqref generates cell reference sequence by given cells coordinates list.
+func (f *File) squashSqref(cells [][]int) []string {
+ if len(cells) == 1 {
+ cell, _ := CoordinatesToCellName(cells[0][0], cells[0][1])
+ return []string{cell}
+ } else if len(cells) == 0 {
+ return []string{}
+ }
+ l, r, res := 0, 0, []string{}
+ for i := 1; i < len(cells); i++ {
+ if cells[i][0] == cells[r][0] && cells[i][1]-cells[r][1] > 1 {
+ curr, _ := f.coordinatesToAreaRef(append(cells[l], cells[r]...))
+ if l == r {
+ curr, _ = CoordinatesToCellName(cells[l][0], cells[l][1])
+ }
+ res = append(res, curr)
+ l, r = i, i
+ } else {
+ r++
+ }
+ }
+ curr, _ := f.coordinatesToAreaRef(append(cells[l], cells[r]...))
+ if l == r {
+ curr, _ = CoordinatesToCellName(cells[l][0], cells[l][1])
+ }
+ return append(res, curr)
+}
diff --git a/datavalidation_test.go b/datavalidation_test.go
index 13a0053..f0afe5f 100644
--- a/datavalidation_test.go
+++ b/datavalidation_test.go
@@ -129,10 +129,36 @@ func TestDeleteDataValidation(t *testing.T) {
assert.NoError(t, dvRange.SetRange(10, 20, DataValidationTypeWhole, DataValidationOperatorBetween))
dvRange.SetInput("input title", "input body")
assert.NoError(t, f.AddDataValidation("Sheet1", dvRange))
-
assert.NoError(t, f.DeleteDataValidation("Sheet1", "A1:B2"))
+
+ dvRange.Sqref = "A1"
+ assert.NoError(t, f.AddDataValidation("Sheet1", dvRange))
+ assert.NoError(t, f.DeleteDataValidation("Sheet1", "B1"))
+ assert.NoError(t, f.DeleteDataValidation("Sheet1", "A1"))
+
+ dvRange.Sqref = "C2:C5"
+ assert.NoError(t, f.AddDataValidation("Sheet1", dvRange))
+ assert.NoError(t, f.DeleteDataValidation("Sheet1", "C4"))
+
+ dvRange = NewDataValidation(true)
+ dvRange.Sqref = "D2:D2 D3 D4"
+ assert.NoError(t, dvRange.SetRange(10, 20, DataValidationTypeWhole, DataValidationOperatorBetween))
+ dvRange.SetInput("input title", "input body")
+ assert.NoError(t, f.AddDataValidation("Sheet1", dvRange))
+ assert.NoError(t, f.DeleteDataValidation("Sheet1", "D3"))
+
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestDeleteDataValidation.xlsx")))
+ dvRange.Sqref = "A"
+ assert.NoError(t, f.AddDataValidation("Sheet1", dvRange))
+ assert.EqualError(t, f.DeleteDataValidation("Sheet1", "A1"), `cannot convert cell "A" to coordinates: invalid cell name "A"`)
+
+ assert.EqualError(t, f.DeleteDataValidation("Sheet1", "A1:A"), `cannot convert cell "A" to coordinates: invalid cell name "A"`)
+ ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml")
+ assert.True(t, ok)
+ ws.(*xlsxWorksheet).DataValidations.DataValidation[0].Sqref = "A1:A"
+ assert.EqualError(t, f.DeleteDataValidation("Sheet1", "A1:B2"), `cannot convert cell "A" to coordinates: invalid cell name "A"`)
+
// Test delete data validation on no exists worksheet.
assert.EqualError(t, f.DeleteDataValidation("SheetN", "A1:B2"), "sheet SheetN is not exist")
}
diff --git a/lib.go b/lib.go
index df2af4a..7db14c4 100644
--- a/lib.go
+++ b/lib.go
@@ -219,6 +219,114 @@ func CoordinatesToCellName(col, row int, abs ...bool) (string, error) {
return sign + colname + sign + strconv.Itoa(row), err
}
+// areaRefToCoordinates provides a function to convert area reference to a
+// pair of coordinates.
+func (f *File) areaRefToCoordinates(ref string) ([]int, error) {
+ rng := strings.Split(strings.Replace(ref, "$", "", -1), ":")
+ if len(rng) < 2 {
+ return nil, ErrParameterInvalid
+ }
+
+ return areaRangeToCoordinates(rng[0], rng[1])
+}
+
+// areaRangeToCoordinates provides a function to convert cell range to a
+// pair of coordinates.
+func areaRangeToCoordinates(firstCell, lastCell string) ([]int, error) {
+ coordinates := make([]int, 4)
+ var err error
+ coordinates[0], coordinates[1], err = CellNameToCoordinates(firstCell)
+ if err != nil {
+ return coordinates, err
+ }
+ coordinates[2], coordinates[3], err = CellNameToCoordinates(lastCell)
+ return coordinates, err
+}
+
+// sortCoordinates provides a function to correct the coordinate area, such
+// correct C1:B3 to B1:C3.
+func sortCoordinates(coordinates []int) error {
+ if len(coordinates) != 4 {
+ return ErrCoordinates
+ }
+ if coordinates[2] < coordinates[0] {
+ coordinates[2], coordinates[0] = coordinates[0], coordinates[2]
+ }
+ if coordinates[3] < coordinates[1] {
+ coordinates[3], coordinates[1] = coordinates[1], coordinates[3]
+ }
+ return nil
+}
+
+// coordinatesToAreaRef provides a function to convert a pair of coordinates
+// to area reference.
+func (f *File) coordinatesToAreaRef(coordinates []int) (string, error) {
+ if len(coordinates) != 4 {
+ return "", ErrCoordinates
+ }
+ firstCell, err := CoordinatesToCellName(coordinates[0], coordinates[1])
+ if err != nil {
+ return "", err
+ }
+ lastCell, err := CoordinatesToCellName(coordinates[2], coordinates[3])
+ if err != nil {
+ return "", err
+ }
+ return firstCell + ":" + lastCell, err
+}
+
+// flatSqref convert reference sequence to cell coordinates list.
+func (f *File) flatSqref(sqref string) (cells map[int][][]int, err error) {
+ var coordinates []int
+ cells = make(map[int][][]int)
+ for _, ref := range strings.Fields(sqref) {
+ rng := strings.Split(ref, ":")
+ switch len(rng) {
+ case 1:
+ var col, row int
+ col, row, err = CellNameToCoordinates(rng[0])
+ if err != nil {
+ return
+ }
+ cells[col] = append(cells[col], []int{col, row})
+ case 2:
+ if coordinates, err = f.areaRefToCoordinates(ref); err != nil {
+ return
+ }
+ _ = sortCoordinates(coordinates)
+ for c := coordinates[0]; c <= coordinates[2]; c++ {
+ for r := coordinates[1]; r <= coordinates[3]; r++ {
+ cells[c] = append(cells[c], []int{c, r})
+ }
+ }
+ }
+ }
+ return
+}
+
+// inCoordinates provides a method to check if an coordinate is present in
+// coordinates array, and return the index of its location, otherwise
+// return -1.
+func inCoordinates(a [][]int, x []int) int {
+ for idx, n := range a {
+ if x[0] == n[0] && x[1] == n[1] {
+ return idx
+ }
+ }
+ return -1
+}
+
+// inStrSlice provides a method to check if an element is present in an array,
+// and return the index of its location, otherwise return -1.
+func inStrSlice(a []string, x string) int {
+ for idx, n := range a {
+ if x == n {
+ return idx
+ }
+ }
+ return -1
+}
+
// boolPtr returns a pointer to a bool with the given value.
func boolPtr(b bool) *bool { return &b }
diff --git a/lib_test.go b/lib_test.go
index 315688f..2e0e506 100644
--- a/lib_test.go
+++ b/lib_test.go
@@ -211,6 +211,27 @@ func TestCoordinatesToCellName_Error(t *testing.T) {
}
}
+func TestCoordinatesToAreaRef(t *testing.T) {
+ f := NewFile()
+ _, err := f.coordinatesToAreaRef([]int{})
+ assert.EqualError(t, err, ErrCoordinates.Error())
+ _, err = f.coordinatesToAreaRef([]int{1, -1, 1, 1})
+ assert.EqualError(t, err, "invalid cell coordinates [1, -1]")
+ _, err = f.coordinatesToAreaRef([]int{1, 1, 1, -1})
+ assert.EqualError(t, err, "invalid cell coordinates [1, -1]")
+ ref, err := f.coordinatesToAreaRef([]int{1, 1, 1, 1})
+ assert.NoError(t, err)
+ assert.EqualValues(t, ref, "A1:A1")
+}
+
+func TestSortCoordinates(t *testing.T) {
+ assert.EqualError(t, sortCoordinates(make([]int, 3)), ErrCoordinates.Error())
+}
+
+func TestInStrSlice(t *testing.T) {
+ assert.EqualValues(t, -1, inStrSlice([]string{}, ""))
+}
+
func TestBytesReplace(t *testing.T) {
s := []byte{0x01}
assert.EqualValues(t, s, bytesReplace(s, []byte{}, []byte{}, 0))
diff --git a/pivotTable.go b/pivotTable.go
index f6d6d2d..07cf84c 100644
--- a/pivotTable.go
+++ b/pivotTable.go
@@ -459,17 +459,6 @@ func (f *File) addPivotDataFields(pt *xlsxPivotTableDefinition, opt *PivotTableO
return err
}
-// inStrSlice provides a method to check if an element is present in an array,
-// and return the index of its location, otherwise return -1.
-func inStrSlice(a []string, x string) int {
- for idx, n := range a {
- if x == n {
- return idx
- }
- }
- return -1
-}
-
// inPivotTableField provides a method to check if an element is present in
// pivot table fields list, and return the index of its location, otherwise
// return -1.
diff --git a/pivotTable_test.go b/pivotTable_test.go
index bf6bb01..dbb8252 100644
--- a/pivotTable_test.go
+++ b/pivotTable_test.go
@@ -301,10 +301,6 @@ func TestGetPivotFieldsOrder(t *testing.T) {
assert.EqualError(t, err, "sheet SheetN is not exist")
}
-func TestInStrSlice(t *testing.T) {
- assert.EqualValues(t, -1, inStrSlice([]string{}, ""))
-}
-
func TestGetPivotTableFieldName(t *testing.T) {
f := NewFile()
f.getPivotTableFieldName("-", []PivotTableField{})