From 317ef65381b179b863dcd6b1f5479cc576a8376c Mon Sep 17 00:00:00 2001 From: mbresson Date: Fri, 19 Jan 2018 17:32:54 +0800 Subject: [PATCH] make SetCellStyle quicker by skipping conversions in checkCellInArea, and skipping area checks when we are sure the cell can't be before or past the current row/col Signed-off-by: Matthieu Bresson --- cell.go | 25 +++++++--------------- cell_test.go | 40 +++++++++++++++++++++++++++++++++++ lib.go | 41 ++++++++++++++++++++++++++++++++++++ lib_test.go | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++ styles.go | 12 +++++++++++ 5 files changed, 159 insertions(+), 18 deletions(-) create mode 100644 cell_test.go create mode 100644 lib_test.go diff --git a/cell.go b/cell.go index 5d26338..bb363aa 100644 --- a/cell.go +++ b/cell.go @@ -455,27 +455,16 @@ func (f *File) SetCellDefault(sheet, axis, value string) { // checkCellInArea provides function to determine if a given coordinate is // within an area. func checkCellInArea(cell, area string) bool { - result := false cell = strings.ToUpper(cell) - col := string(strings.Map(letterOnlyMapF, cell)) - row, _ := strconv.Atoi(strings.Map(intOnlyMapF, cell)) - xAxis := row - 1 - yAxis := TitleToNumber(col) + area = strings.ToUpper(area) ref := strings.Split(area, ":") - hCol := string(strings.Map(letterOnlyMapF, ref[0])) - hRow, _ := strconv.Atoi(strings.Map(intOnlyMapF, ref[0])) - hyAxis := hRow - 1 - hxAxis := TitleToNumber(hCol) + from := ref[0] + to := ref[1] - vCol := string(strings.Map(letterOnlyMapF, ref[1])) - vRow, _ := strconv.Atoi(strings.Map(intOnlyMapF, ref[1])) - vyAxis := vRow - 1 - vxAxis := TitleToNumber(vCol) - - if hxAxis <= yAxis && yAxis <= vxAxis && hyAxis <= xAxis && xAxis <= vyAxis { - result = true - } + col, row := getCellColRow(cell) + fromCol, fromRow := getCellColRow(from) + toCol, toRow := getCellColRow(to) - return result + return axisLowerOrEqualThan(fromCol, col) && axisLowerOrEqualThan(col, toCol) && axisLowerOrEqualThan(fromRow, row) && axisLowerOrEqualThan(row, toRow) } diff --git a/cell_test.go b/cell_test.go new file mode 100644 index 0000000..2ab5413 --- /dev/null +++ b/cell_test.go @@ -0,0 +1,40 @@ +package excelize + +import "testing" + +func TestCheckCellInArea(t *testing.T) { + expectedTrueCellInAreaList := [][2]string{ + [2]string{"c2", "A1:AAZ32"}, + [2]string{"AA0", "Z0:AB1"}, + [2]string{"B9", "A1:B9"}, + [2]string{"C2", "C2:C2"}, + } + + for _, expectedTrueCellInArea := range expectedTrueCellInAreaList { + cell := expectedTrueCellInArea[0] + area := expectedTrueCellInArea[1] + + cellInArea := checkCellInArea(cell, area) + + if !cellInArea { + t.Fatalf("Expected cell %v to be in area %v, got false\n", cell, area) + } + } + + expectedFalseCellInAreaList := [][2]string{ + [2]string{"c2", "A4:AAZ32"}, + [2]string{"C4", "D6:A1"}, // weird case, but you never know + [2]string{"AEF42", "BZ40:AEF41"}, + } + + for _, expectedFalseCellInArea := range expectedFalseCellInAreaList { + cell := expectedFalseCellInArea[0] + area := expectedFalseCellInArea[1] + + cellInArea := checkCellInArea(cell, area) + + if cellInArea { + t.Fatalf("Expected cell %v not to be inside of area %v, but got true\n", cell, area) + } + } +} diff --git a/lib.go b/lib.go index 2f3df35..c0426d6 100644 --- a/lib.go +++ b/lib.go @@ -7,6 +7,7 @@ import ( "io" "log" "math" + "unicode" ) // ReadZipReader can be used to read an XLSX in memory without touching the @@ -132,3 +133,43 @@ func defaultTrue(b *bool) bool { } return *b } + +// axisLowerOrEqualThan returns true if axis1 <= axis2 +// axis1/axis2 can be either a column or a row axis, e.g. "A", "AAE", "42", "1", etc. +// +// For instance, the following comparisons are all true: +// +// "A" <= "B" +// "A" <= "AA" +// "B" <= "AA" +// "BC" <= "ABCD" (in a XLSX sheet, the BC col comes before the ABCD col) +// "1" <= "2" +// "2" <= "11" (in a XLSX sheet, the row 2 comes before the row 11) +// and so on +func axisLowerOrEqualThan(axis1, axis2 string) bool { + if len(axis1) < len(axis2) { + return true + } else if len(axis1) > len(axis2) { + return false + } else { + return axis1 <= axis2 + } +} + +// getCellColRow returns the two parts of a cell identifier (its col and row) as strings +// +// For instance: +// +// "C220" => "C", "220" +// "aaef42" => "aaef", "42" +// "" => "", "" +func getCellColRow(cell string) (col, row string) { + for index, rune := range cell { + if unicode.IsDigit(rune) { + return cell[:index], cell[index:] + } + + } + + return cell, "" +} diff --git a/lib_test.go b/lib_test.go new file mode 100644 index 0000000..3a4dc2c --- /dev/null +++ b/lib_test.go @@ -0,0 +1,59 @@ +package excelize + +import "testing" + +func TestAxisLowerOrEqualThan(t *testing.T) { + trueExpectedInputList := [][2]string{ + [2]string{"A", "B"}, + [2]string{"A", "AA"}, + [2]string{"B", "AA"}, + [2]string{"BC", "ABCD"}, + [2]string{"1", "2"}, + [2]string{"2", "11"}, + } + + for _, trueExpectedInput := range trueExpectedInputList { + isLowerOrEqual := axisLowerOrEqualThan(trueExpectedInput[0], trueExpectedInput[1]) + if !isLowerOrEqual { + t.Fatalf("Expected %v <= %v = true, got false\n", trueExpectedInput[0], trueExpectedInput[1]) + } + } + + falseExpectedInputList := [][2]string{ + [2]string{"B", "A"}, + [2]string{"AA", "A"}, + [2]string{"AA", "B"}, + [2]string{"ABCD", "AB"}, + [2]string{"2", "1"}, + [2]string{"11", "2"}, + } + + for _, falseExpectedInput := range falseExpectedInputList { + isLowerOrEqual := axisLowerOrEqualThan(falseExpectedInput[0], falseExpectedInput[1]) + if isLowerOrEqual { + t.Fatalf("Expected %v <= %v = false, got true\n", falseExpectedInput[0], falseExpectedInput[1]) + } + } +} + +func TestGetCellColRow(t *testing.T) { + cellExpectedColRowList := map[string][2]string{ + "C220": [2]string{"C", "220"}, + "aaef42": [2]string{"aaef", "42"}, + "bonjour": [2]string{"bonjour", ""}, + "59": [2]string{"", "59"}, + "": [2]string{"", ""}, + } + + for cell, expectedColRow := range cellExpectedColRowList { + col, row := getCellColRow(cell) + + if col != expectedColRow[0] { + t.Fatalf("Expected cell %v to return col %v, got col %v\n", cell, expectedColRow[0], col) + } + + if row != expectedColRow[1] { + t.Fatalf("Expected cell %v to return row %v, got row %v\n", cell, expectedColRow[1], row) + } + } +} diff --git a/styles.go b/styles.go index 87bca13..8cee5ff 100644 --- a/styles.go +++ b/styles.go @@ -2327,7 +2327,19 @@ func (f *File) SetCellStyle(sheet, hcell, vcell string, styleID int) { completeCol(xlsx, vyAxis+1, vxAxis+1) for r, row := range xlsx.SheetData.Row { + if r < hyAxis { + continue + } else if r > vyAxis { + break + } + for k, c := range row.C { + if k < hxAxis { + continue + } else if k > vxAxis { + break + } + if checkCellInArea(c.R, hcell+":"+vcell) { xlsx.SheetData.Row[r].C[k].S = styleID }