summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--adjust.go12
-rw-r--r--adjust_test.go4
-rw-r--r--calc.go116
-rw-r--r--lib.go13
-rw-r--r--sheet.go20
-rw-r--r--sheet_test.go13
-rw-r--r--styles.go4
-rw-r--r--table.go6
8 files changed, 127 insertions, 61 deletions
diff --git a/adjust.go b/adjust.go
index f1ae536..c391cb1 100644
--- a/adjust.go
+++ b/adjust.go
@@ -1,4 +1,4 @@
-// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
+// Copyright 2016 - 2021 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
@@ -39,6 +39,7 @@ func (f *File) adjustHelper(sheet string, dir adjustDirection, num, offset int)
if err != nil {
return err
}
+ sheetID := f.getSheetID(sheet)
if dir == rows {
f.adjustRowDimensions(ws, num, offset)
} else {
@@ -51,7 +52,7 @@ func (f *File) adjustHelper(sheet string, dir adjustDirection, num, offset int)
if err = f.adjustAutoFilter(ws, dir, num, offset); err != nil {
return err
}
- if err = f.adjustCalcChain(dir, num, offset); err != nil {
+ if err = f.adjustCalcChain(dir, num, offset, sheetID); err != nil {
return err
}
checkSheet(ws)
@@ -197,7 +198,7 @@ func (f *File) adjustAutoFilterHelper(dir adjustDirection, coordinates []int, nu
// areaRefToCoordinates provides a function to convert area reference to a
// pair of coordinates.
func (f *File) areaRefToCoordinates(ref string) ([]int, error) {
- rng := strings.Split(ref, ":")
+ rng := strings.Split(strings.Replace(ref, "$", "", -1), ":")
return areaRangeToCoordinates(rng[0], rng[1])
}
@@ -310,11 +311,14 @@ func (f *File) deleteMergeCell(ws *xlsxWorksheet, idx int) {
// adjustCalcChain provides a function to update the calculation chain when
// inserting or deleting rows or columns.
-func (f *File) adjustCalcChain(dir adjustDirection, num, offset int) error {
+func (f *File) adjustCalcChain(dir adjustDirection, num, offset, sheetID int) error {
if f.CalcChain == nil {
return nil
}
for index, c := range f.CalcChain.C {
+ if c.I != sheetID {
+ continue
+ }
colNum, rowNum, err := CellNameToCoordinates(c.R)
if err != nil {
return err
diff --git a/adjust_test.go b/adjust_test.go
index 3997bd9..bdbaebe 100644
--- a/adjust_test.go
+++ b/adjust_test.go
@@ -98,13 +98,13 @@ func TestAdjustCalcChain(t *testing.T) {
f := NewFile()
f.CalcChain = &xlsxCalcChain{
C: []xlsxCalcChainC{
- {R: "B2"},
+ {R: "B2", I: 2}, {R: "B2", I: 1},
},
}
assert.NoError(t, f.InsertCol("Sheet1", "A"))
assert.NoError(t, f.InsertRow("Sheet1", 1))
- f.CalcChain.C[0].R = "invalid coordinates"
+ f.CalcChain.C[1].R = "invalid coordinates"
assert.EqualError(t, f.InsertCol("Sheet1", "A"), `cannot convert cell "invalid coordinates" to coordinates: invalid cell name "invalid coordinates"`)
f.CalcChain = nil
assert.NoError(t, f.InsertCol("Sheet1", "A"))
diff --git a/calc.go b/calc.go
index cb7d2f8..fd03918 100644
--- a/calc.go
+++ b/calc.go
@@ -1,4 +1,4 @@
-// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
+// Copyright 2016 - 2021 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
@@ -288,7 +288,7 @@ func getPriority(token efp.Token) (pri int) {
if token.TValue == "-" && token.TType == efp.TokenTypeOperatorPrefix {
pri = 6
}
- if token.TSubType == efp.TokenSubTypeStart && token.TType == efp.TokenTypeSubexpression { // (
+ if isBeginParenthesesToken(token) { // (
pri = 0
}
return
@@ -356,7 +356,7 @@ func (f *File) evalInfixExp(sheet string, tokens []efp.Token) (efp.Token, error)
}
// function start
- if token.TType == efp.TokenTypeFunction && token.TSubType == efp.TokenSubTypeStart {
+ if isFunctionStartToken(token) {
opfStack.Push(token)
argsStack.Push(list.New().Init())
continue
@@ -436,44 +436,8 @@ func (f *File) evalInfixExp(sheet string, tokens []efp.Token) (efp.Token, error)
Type: ArgString,
})
}
-
- // current token is function stop
- if token.TType == efp.TokenTypeFunction && token.TSubType == efp.TokenSubTypeStop {
- for !opftStack.Empty() {
- // calculate trigger
- topOpt := opftStack.Peek().(efp.Token)
- if err := calculate(opfdStack, topOpt); err != nil {
- return efp.Token{}, err
- }
- opftStack.Pop()
- }
-
- // push opfd to args
- if opfdStack.Len() > 0 {
- argsStack.Peek().(*list.List).PushBack(formulaArg{
- String: opfdStack.Pop().(efp.Token).TValue,
- Type: ArgString,
- })
- }
- // call formula function to evaluate
- arg := callFuncByName(&formulaFuncs{}, strings.NewReplacer(
- "_xlfn", "", ".", "").Replace(opfStack.Peek().(efp.Token).TValue),
- []reflect.Value{reflect.ValueOf(argsStack.Peek().(*list.List))})
- if arg.Type == ArgError {
- return efp.Token{}, errors.New(arg.Value())
- }
- argsStack.Pop()
- opfStack.Pop()
- if opfStack.Len() > 0 { // still in function stack
- if nextToken.TType == efp.TokenTypeOperatorInfix {
- // mathematics calculate in formula function
- opfdStack.Push(efp.Token{TValue: arg.Value(), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
- } else {
- argsStack.Peek().(*list.List).PushBack(arg)
- }
- } else {
- opdStack.Push(efp.Token{TValue: arg.Value(), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
- }
+ if err = evalInfixExpFunc(token, nextToken, opfStack, opdStack, opftStack, opfdStack, argsStack); err != nil {
+ return efp.Token{}, err
}
}
}
@@ -490,6 +454,50 @@ func (f *File) evalInfixExp(sheet string, tokens []efp.Token) (efp.Token, error)
return opdStack.Peek().(efp.Token), err
}
+// evalInfixExpFunc evaluate formula function in the infix expression.
+func evalInfixExpFunc(token, nextToken efp.Token, opfStack, opdStack, opftStack, opfdStack, argsStack *Stack) error {
+ if !isFunctionStopToken(token) {
+ return nil
+ }
+ // current token is function stop
+ for !opftStack.Empty() {
+ // calculate trigger
+ topOpt := opftStack.Peek().(efp.Token)
+ if err := calculate(opfdStack, topOpt); err != nil {
+ return err
+ }
+ opftStack.Pop()
+ }
+
+ // push opfd to args
+ if opfdStack.Len() > 0 {
+ argsStack.Peek().(*list.List).PushBack(formulaArg{
+ String: opfdStack.Pop().(efp.Token).TValue,
+ Type: ArgString,
+ })
+ }
+ // call formula function to evaluate
+ arg := callFuncByName(&formulaFuncs{}, strings.NewReplacer(
+ "_xlfn", "", ".", "").Replace(opfStack.Peek().(efp.Token).TValue),
+ []reflect.Value{reflect.ValueOf(argsStack.Peek().(*list.List))})
+ if arg.Type == ArgError {
+ return errors.New(arg.Value())
+ }
+ argsStack.Pop()
+ opfStack.Pop()
+ if opfStack.Len() > 0 { // still in function stack
+ if nextToken.TType == efp.TokenTypeOperatorInfix {
+ // mathematics calculate in formula function
+ opfdStack.Push(efp.Token{TValue: arg.Value(), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
+ } else {
+ argsStack.Peek().(*list.List).PushBack(arg)
+ }
+ } else {
+ opdStack.Push(efp.Token{TValue: arg.Value(), TType: efp.TokenTypeOperand, TSubType: efp.TokenSubTypeNumber})
+ }
+ return nil
+}
+
// calcPow evaluate exponentiation arithmetic operations.
func calcPow(rOpd, lOpd string, opdStack *Stack) error {
lOpdVal, err := strconv.ParseFloat(lOpd, 64)
@@ -722,6 +730,26 @@ func (f *File) parseOperatorPrefixToken(optStack, opdStack *Stack, token efp.Tok
return
}
+// isFunctionStartToken determine if the token is function stop.
+func isFunctionStartToken(token efp.Token) bool {
+ return token.TType == efp.TokenTypeFunction && token.TSubType == efp.TokenSubTypeStart
+}
+
+// isFunctionStopToken determine if the token is function stop.
+func isFunctionStopToken(token efp.Token) bool {
+ return token.TType == efp.TokenTypeFunction && token.TSubType == efp.TokenSubTypeStop
+}
+
+// isBeginParenthesesToken determine if the token is begin parentheses: (.
+func isBeginParenthesesToken(token efp.Token) bool {
+ return token.TType == efp.TokenTypeSubexpression && token.TSubType == efp.TokenSubTypeStart
+}
+
+// isEndParenthesesToken determine if the token is end parentheses: ).
+func isEndParenthesesToken(token efp.Token) bool {
+ return token.TType == efp.TokenTypeSubexpression && token.TSubType == efp.TokenSubTypeStop
+}
+
// isOperatorPrefixToken determine if the token is parse operator prefix
// token.
func isOperatorPrefixToken(token efp.Token) bool {
@@ -771,11 +799,11 @@ func (f *File) parseToken(sheet string, token efp.Token, opdStack, optStack *Sta
return err
}
}
- if token.TType == efp.TokenTypeSubexpression && token.TSubType == efp.TokenSubTypeStart { // (
+ if isBeginParenthesesToken(token) { // (
optStack.Push(token)
}
- if token.TType == efp.TokenTypeSubexpression && token.TSubType == efp.TokenSubTypeStop { // )
- for optStack.Peek().(efp.Token).TSubType != efp.TokenSubTypeStart && optStack.Peek().(efp.Token).TType != efp.TokenTypeSubexpression { // != (
+ if isEndParenthesesToken(token) { // )
+ for !isBeginParenthesesToken(optStack.Peek().(efp.Token)) { // != (
topOpt := optStack.Peek().(efp.Token)
if err := calculate(opdStack, topOpt); err != nil {
return err
diff --git a/lib.go b/lib.go
index 3f13512..0ebe468 100644
--- a/lib.go
+++ b/lib.go
@@ -1,4 +1,4 @@
-// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
+// Copyright 2016 - 2021 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
@@ -201,13 +201,20 @@ func CellNameToCoordinates(cell string) (int, int, error) {
// Example:
//
// excelize.CoordinatesToCellName(1, 1) // returns "A1", nil
+// excelize.CoordinatesToCellName(1, 1, true) // returns "$A$1", nil
//
-func CoordinatesToCellName(col, row int) (string, error) {
+func CoordinatesToCellName(col, row int, abs ...bool) (string, error) {
if col < 1 || row < 1 {
return "", fmt.Errorf("invalid cell coordinates [%d, %d]", col, row)
}
+ sign := ""
+ for _, a := range abs {
+ if a {
+ sign = "$"
+ }
+ }
colname, err := ColumnNumberToName(col)
- return colname + strconv.Itoa(row), err
+ return sign + colname + sign + strconv.Itoa(row), err
}
// boolPtr returns a pointer to a bool with the given value.
diff --git a/sheet.go b/sheet.go
index 9b80395..bb94f6a 100644
--- a/sheet.go
+++ b/sheet.go
@@ -1,4 +1,4 @@
-// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
+// Copyright 2016 - 2021 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
@@ -500,6 +500,22 @@ func (f *File) DeleteSheet(name string) {
wb := f.workbookReader()
wbRels := f.relsReader(f.getWorkbookRelsPath())
activeSheetName := f.GetSheetName(f.GetActiveSheetIndex())
+ deleteSheetID := f.getSheetID(name)
+ // Delete and adjust defined names
+ if wb.DefinedNames != nil {
+ for idx := 0; idx < len(wb.DefinedNames.DefinedName); idx++ {
+ dn := wb.DefinedNames.DefinedName[idx]
+ if dn.LocalSheetID != nil {
+ sheetID := *dn.LocalSheetID + 1
+ if sheetID == deleteSheetID {
+ wb.DefinedNames.DefinedName = append(wb.DefinedNames.DefinedName[:idx], wb.DefinedNames.DefinedName[idx+1:]...)
+ idx--
+ } else if sheetID > deleteSheetID {
+ wb.DefinedNames.DefinedName[idx].LocalSheetID = intPtr(*dn.LocalSheetID - 1)
+ }
+ }
+ }
+ }
for idx, sheet := range wb.Sheets.Sheet {
if sheet.Name == sheetName {
wb.Sheets.Sheet = append(wb.Sheets.Sheet[:idx], wb.Sheets.Sheet[idx+1:]...)
@@ -517,7 +533,7 @@ func (f *File) DeleteSheet(name string) {
}
target := f.deleteSheetFromWorkbookRels(sheet.ID)
f.deleteSheetFromContentTypes(target)
- f.deleteCalcChain(sheet.SheetID, "") // Delete CalcChain
+ f.deleteCalcChain(sheet.SheetID, "")
delete(f.sheetMap, sheetName)
delete(f.XLSX, sheetXML)
delete(f.XLSX, rels)
diff --git a/sheet_test.go b/sheet_test.go
index d68327e..f218da7 100644
--- a/sheet_test.go
+++ b/sheet_test.go
@@ -383,6 +383,19 @@ func TestDeleteSheet(t *testing.T) {
f.DeleteSheet("Sheet1")
assert.Equal(t, "Sheet2", f.GetSheetName(f.GetActiveSheetIndex()))
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestDeleteSheet.xlsx")))
+ // Test with auto filter defined names
+ f = NewFile()
+ f.NewSheet("Sheet2")
+ f.NewSheet("Sheet3")
+ assert.NoError(t, f.SetCellValue("Sheet1", "A1", "A"))
+ assert.NoError(t, f.SetCellValue("Sheet2", "A1", "A"))
+ assert.NoError(t, f.SetCellValue("Sheet3", "A1", "A"))
+ assert.NoError(t, f.AutoFilter("Sheet1", "A1", "A1", ""))
+ assert.NoError(t, f.AutoFilter("Sheet2", "A1", "A1", ""))
+ assert.NoError(t, f.AutoFilter("Sheet3", "A1", "A1", ""))
+ f.DeleteSheet("Sheet2")
+ f.DeleteSheet("Sheet1")
+ assert.NoError(t, f.SaveAs(filepath.Join("test", "TestDeleteSheet2.xlsx")))
}
func BenchmarkNewSheet(b *testing.B) {
diff --git a/styles.go b/styles.go
index 9fd0f18..851332b 100644
--- a/styles.go
+++ b/styles.go
@@ -1,4 +1,4 @@
-// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
+// Copyright 2016 - 2021 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
@@ -2410,8 +2410,6 @@ func newFills(style *Style, fg bool) *xlsxFill {
gradient.Left = 0.5
gradient.Right = 0.5
gradient.Top = 0.5
- default:
- break
}
var stops []*xlsxGradientFillStop
for index, color := range style.Fill.Color {
diff --git a/table.go b/table.go
index 59f1cfe..8775929 100644
--- a/table.go
+++ b/table.go
@@ -1,4 +1,4 @@
-// Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
+// Copyright 2016 - 2021 The excelize Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
@@ -287,8 +287,8 @@ func (f *File) AutoFilter(sheet, hcell, vcell, format string) error {
}
formatSet, _ := parseAutoFilterSet(format)
- cellStart, _ := CoordinatesToCellName(hcol, hrow)
- cellEnd, _ := CoordinatesToCellName(vcol, vrow)
+ cellStart, _ := CoordinatesToCellName(hcol, hrow, true)
+ cellEnd, _ := CoordinatesToCellName(vcol, vrow, true)
ref, filterDB := cellStart+":"+cellEnd, "_xlnm._FilterDatabase"
wb := f.workbookReader()
sheetID := f.GetSheetIndex(sheet)