Skip to content

Commit

Permalink
- Support concurrency iterate rows and columns
Browse files Browse the repository at this point in the history
- Rename exported field `File.XLSX` to `File.Pkg`
- Exported error message
  • Loading branch information
xuri committed Jul 4, 2021
1 parent 0e02329 commit 544ef18
Show file tree
Hide file tree
Showing 38 changed files with 377 additions and 262 deletions.
18 changes: 4 additions & 14 deletions adjust_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,20 +73,10 @@ func TestAdjustAutoFilter(t *testing.T) {
func TestAdjustHelper(t *testing.T) {
f := NewFile()
f.NewSheet("Sheet2")
f.Sheet["xl/worksheets/sheet1.xml"] = &xlsxWorksheet{
MergeCells: &xlsxMergeCells{
Cells: []*xlsxMergeCell{
{
Ref: "A:B1",
},
},
},
}
f.Sheet["xl/worksheets/sheet2.xml"] = &xlsxWorksheet{
AutoFilter: &xlsxAutoFilter{
Ref: "A1:B",
},
}
f.Sheet.Store("xl/worksheets/sheet1.xml", &xlsxWorksheet{
MergeCells: &xlsxMergeCells{Cells: []*xlsxMergeCell{{Ref: "A:B1"}}}})
f.Sheet.Store("xl/worksheets/sheet2.xml", &xlsxWorksheet{
AutoFilter: &xlsxAutoFilter{Ref: "A1:B"}})
// testing adjustHelper with illegal cell coordinates.
assert.EqualError(t, f.adjustHelper("Sheet1", rows, 0, 0), `cannot convert cell "A" to coordinates: invalid cell name "A"`)
assert.EqualError(t, f.adjustHelper("Sheet2", rows, 0, 0), `cannot convert cell "B" to coordinates: invalid cell name "B"`)
Expand Down
2 changes: 1 addition & 1 deletion calcchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func (f *File) deleteCalcChain(index int, axis string) {
}
if len(calc.C) == 0 {
f.CalcChain = nil
delete(f.XLSX, "xl/calcChain.xml")
f.Pkg.Delete("xl/calcChain.xml")
content := f.contentTypesReader()
for k, v := range content.Overrides {
if v.PartName == "/xl/calcChain.xml" {
Expand Down
2 changes: 1 addition & 1 deletion calcchain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import "testing"
func TestCalcChainReader(t *testing.T) {
f := NewFile()
f.CalcChain = nil
f.XLSX["xl/calcChain.xml"] = MacintoshCyrillicCharset
f.Pkg.Store("xl/calcChain.xml", MacintoshCyrillicCharset)
f.calcChainReader()
}

Expand Down
12 changes: 7 additions & 5 deletions cell.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ package excelize

import (
"encoding/xml"
"errors"
"fmt"
"reflect"
"strconv"
Expand Down Expand Up @@ -187,6 +186,8 @@ func (f *File) SetCellInt(sheet, axis string, value int) error {
if err != nil {
return err
}
ws.Lock()
defer ws.Unlock()
cellData.S = f.prepareCellStyle(ws, col, cellData.S)
cellData.T, cellData.V = setCellInt(value)
return err
Expand Down Expand Up @@ -262,6 +263,8 @@ func (f *File) SetCellStr(sheet, axis, value string) error {
if err != nil {
return err
}
ws.Lock()
defer ws.Unlock()
cellData.S = f.prepareCellStyle(ws, col, cellData.S)
cellData.T, cellData.V = f.setCellString(value)
return err
Expand Down Expand Up @@ -742,7 +745,7 @@ func (f *File) SetSheetRow(sheet, axis string, slice interface{}) error {
// Make sure 'slice' is a Ptr to Slice
v := reflect.ValueOf(slice)
if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Slice {
return errors.New("pointer to slice expected")
return ErrParameterInvalid
}
v = v.Elem()

Expand All @@ -762,8 +765,6 @@ func (f *File) SetSheetRow(sheet, axis string, slice interface{}) error {

// getCellInfo does common preparation for all SetCell* methods.
func (f *File) prepareCell(ws *xlsxWorksheet, sheet, cell string) (*xlsxC, int, int, error) {
ws.Lock()
defer ws.Unlock()
var err error
cell, err = f.mergeCellsParser(ws, cell)
if err != nil {
Expand All @@ -775,7 +776,8 @@ func (f *File) prepareCell(ws *xlsxWorksheet, sheet, cell string) (*xlsxC, int,
}

prepareSheetXML(ws, col, row)

ws.Lock()
defer ws.Unlock()
return &ws.SheetData.Row[row-1].C[col-1], col, row, err
}

Expand Down
42 changes: 31 additions & 11 deletions cell_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,20 @@ func TestConcurrency(t *testing.T) {
assert.Equal(t, "", name)
assert.Nil(t, raw)
assert.NoError(t, err)
// Concurrency iterate rows
rows, err := f.Rows("Sheet1")
assert.NoError(t, err)
for rows.Next() {
_, err := rows.Columns()
assert.NoError(t, err)
}
// Concurrency iterate columns
cols, err := f.Cols("Sheet1")
assert.NoError(t, err)
for rows.Next() {
_, err := cols.Rows()
assert.NoError(t, err)
}

wg.Done()
}(i, t)
Expand Down Expand Up @@ -149,8 +163,8 @@ func TestGetCellValue(t *testing.T) {
// Test get cell value without r attribute of the row.
f := NewFile()
sheetData := `<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><sheetData>%s</sheetData></worksheet>`
delete(f.Sheet, "xl/worksheets/sheet1.xml")
f.XLSX["xl/worksheets/sheet1.xml"] = []byte(fmt.Sprintf(sheetData, `<row r="3"><c t="str"><v>A3</v></c></row><row><c t="str"><v>A4</v></c><c t="str"><v>B4</v></c></row><row r="7"><c t="str"><v>A7</v></c><c t="str"><v>B7</v></c></row><row><c t="str"><v>A8</v></c><c t="str"><v>B8</v></c></row>`))
f.Sheet.Delete("xl/worksheets/sheet1.xml")
f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(fmt.Sprintf(sheetData, `<row r="3"><c t="str"><v>A3</v></c></row><row><c t="str"><v>A4</v></c><c t="str"><v>B4</v></c></row><row r="7"><c t="str"><v>A7</v></c><c t="str"><v>B7</v></c></row><row><c t="str"><v>A8</v></c><c t="str"><v>B8</v></c></row>`)))
f.checked = nil
cells := []string{"A3", "A4", "B4", "A7", "B7"}
rows, err := f.GetRows("Sheet1")
Expand All @@ -164,20 +178,20 @@ func TestGetCellValue(t *testing.T) {
cols, err := f.GetCols("Sheet1")
assert.Equal(t, [][]string{{"", "", "A3", "A4", "", "", "A7", "A8"}, {"", "", "", "B4", "", "", "B7", "B8"}}, cols)
assert.NoError(t, err)
delete(f.Sheet, "xl/worksheets/sheet1.xml")
f.XLSX["xl/worksheets/sheet1.xml"] = []byte(fmt.Sprintf(sheetData, `<row r="2"><c r="A2" t="str"><v>A2</v></c></row><row r="2"><c r="B2" t="str"><v>B2</v></c></row>`))
f.Sheet.Delete("xl/worksheets/sheet1.xml")
f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(fmt.Sprintf(sheetData, `<row r="2"><c r="A2" t="str"><v>A2</v></c></row><row r="2"><c r="B2" t="str"><v>B2</v></c></row>`)))
f.checked = nil
cell, err := f.GetCellValue("Sheet1", "A2")
assert.Equal(t, "A2", cell)
assert.NoError(t, err)
delete(f.Sheet, "xl/worksheets/sheet1.xml")
f.XLSX["xl/worksheets/sheet1.xml"] = []byte(fmt.Sprintf(sheetData, `<row r="2"><c r="A2" t="str"><v>A2</v></c></row><row r="2"><c r="B2" t="str"><v>B2</v></c></row>`))
f.Sheet.Delete("xl/worksheets/sheet1.xml")
f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(fmt.Sprintf(sheetData, `<row r="2"><c r="A2" t="str"><v>A2</v></c></row><row r="2"><c r="B2" t="str"><v>B2</v></c></row>`)))
f.checked = nil
rows, err = f.GetRows("Sheet1")
assert.Equal(t, [][]string{nil, {"A2", "B2"}}, rows)
assert.NoError(t, err)
delete(f.Sheet, "xl/worksheets/sheet1.xml")
f.XLSX["xl/worksheets/sheet1.xml"] = []byte(fmt.Sprintf(sheetData, `<row r="1"><c r="A1" t="str"><v>A1</v></c></row><row r="1"><c r="B1" t="str"><v>B1</v></c></row>`))
f.Sheet.Delete("xl/worksheets/sheet1.xml")
f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(fmt.Sprintf(sheetData, `<row r="1"><c r="A1" t="str"><v>A1</v></c></row><row r="1"><c r="B1" t="str"><v>B1</v></c></row>`)))
f.checked = nil
rows, err = f.GetRows("Sheet1")
assert.Equal(t, [][]string{{"A1", "B1"}}, rows)
Expand Down Expand Up @@ -264,17 +278,23 @@ func TestGetCellRichText(t *testing.T) {
assert.True(t, reflect.DeepEqual(runsSource[1].Font, runs[1].Font), "should get the same font")

// Test get cell rich text when string item index overflow
f.Sheet["xl/worksheets/sheet1.xml"].SheetData.Row[0].C[0].V = "2"
ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml")
assert.True(t, ok)
ws.(*xlsxWorksheet).SheetData.Row[0].C[0].V = "2"
runs, err = f.GetCellRichText("Sheet1", "A1")
assert.NoError(t, err)
assert.Equal(t, 0, len(runs))
// Test get cell rich text when string item index is negative
f.Sheet["xl/worksheets/sheet1.xml"].SheetData.Row[0].C[0].V = "-1"
ws, ok = f.Sheet.Load("xl/worksheets/sheet1.xml")
assert.True(t, ok)
ws.(*xlsxWorksheet).SheetData.Row[0].C[0].V = "-1"
runs, err = f.GetCellRichText("Sheet1", "A1")
assert.NoError(t, err)
assert.Equal(t, 0, len(runs))
// Test get cell rich text on invalid string item index
f.Sheet["xl/worksheets/sheet1.xml"].SheetData.Row[0].C[0].V = "x"
ws, ok = f.Sheet.Load("xl/worksheets/sheet1.xml")
assert.True(t, ok)
ws.(*xlsxWorksheet).SheetData.Row[0].C[0].V = "x"
_, err = f.GetCellRichText("Sheet1", "A1")
assert.EqualError(t, err, "strconv.Atoi: parsing \"x\": invalid syntax")
// Test set cell rich text on not exists worksheet
Expand Down
14 changes: 7 additions & 7 deletions chart.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ package excelize
import (
"encoding/json"
"encoding/xml"
"errors"
"fmt"
"strconv"
"strings"
Expand Down Expand Up @@ -945,7 +944,7 @@ func (f *File) AddChartSheet(sheet, format string, combo ...string) error {
sheetID++
path := "xl/chartsheets/sheet" + strconv.Itoa(sheetID) + ".xml"
f.sheetMap[trimSheetName(sheet)] = path
f.Sheet[path] = nil
f.Sheet.Store(path, nil)
drawingID := f.countDrawings() + 1
chartID := f.countCharts() + 1
drawingXML := "xl/drawings/drawing" + strconv.Itoa(drawingID) + ".xml"
Expand Down Expand Up @@ -981,12 +980,12 @@ func (f *File) getFormatChart(format string, combo []string) (*formatChart, []*f
return formatSet, comboCharts, err
}
if _, ok := chartValAxNumFmtFormatCode[comboChart.Type]; !ok {
return formatSet, comboCharts, errors.New("unsupported chart type " + comboChart.Type)
return formatSet, comboCharts, newUnsupportChartType(comboChart.Type)
}
comboCharts = append(comboCharts, comboChart)
}
if _, ok := chartValAxNumFmtFormatCode[formatSet.Type]; !ok {
return formatSet, comboCharts, errors.New("unsupported chart type " + formatSet.Type)
return formatSet, comboCharts, newUnsupportChartType(formatSet.Type)
}
return formatSet, comboCharts, err
}
Expand Down Expand Up @@ -1015,11 +1014,12 @@ func (f *File) DeleteChart(sheet, cell string) (err error) {
// folder xl/charts.
func (f *File) countCharts() int {
count := 0
for k := range f.XLSX {
if strings.Contains(k, "xl/charts/chart") {
f.Pkg.Range(func(k, v interface{}) bool {
if strings.Contains(k.(string), "xl/charts/chart") {
count++
}
}
return true
})
return count
}

Expand Down
14 changes: 9 additions & 5 deletions chart_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,10 @@ func TestChartSize(t *testing.T) {
anchor decodeTwoCellAnchor
)

content, ok := newFile.XLSX["xl/drawings/drawing1.xml"]
content, ok := newFile.Pkg.Load("xl/drawings/drawing1.xml")
assert.True(t, ok, "Can't open the chart")

err = xml.Unmarshal([]byte(content), &workdir)
err = xml.Unmarshal(content.([]byte), &workdir)
if !assert.NoError(t, err) {
t.FailNow()
}
Expand Down Expand Up @@ -340,11 +340,15 @@ func TestChartWithLogarithmicBase(t *testing.T) {
type xmlChartContent []byte
xmlCharts := make([]xmlChartContent, expectedChartsCount)
expectedChartsLogBase := []float64{0, 10.5, 0, 2, 0, 1000}
var ok bool

var (
drawingML interface{}
ok bool
)
for i := 0; i < expectedChartsCount; i++ {
chartPath := fmt.Sprintf("xl/charts/chart%d.xml", i+1)
xmlCharts[i], ok = newFile.XLSX[chartPath]
if drawingML, ok = newFile.Pkg.Load(chartPath); ok {
xmlCharts[i] = drawingML.([]byte)
}
assert.True(t, ok, "Can't open the %s", chartPath)

err = xml.Unmarshal([]byte(xmlCharts[i]), &chartSpaces[i])
Expand Down
7 changes: 5 additions & 2 deletions col.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,8 +199,11 @@ func (f *File) Cols(sheet string) (*Cols, error) {
if !ok {
return nil, ErrSheetNotExist{sheet}
}
if f.Sheet[name] != nil {
output, _ := xml.Marshal(f.Sheet[name])
if ws, ok := f.Sheet.Load(name); ok && ws != nil {
worksheet := ws.(*xlsxWorksheet)
worksheet.Lock()
defer worksheet.Unlock()
output, _ := xml.Marshal(worksheet)
f.saveFileList(name, f.replaceNameSpaceBytes(name, output))
}
var colIterator columnXMLIterator
Expand Down
18 changes: 9 additions & 9 deletions col_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,11 @@ func TestCols(t *testing.T) {
_, err = f.Rows("Sheet1")
assert.NoError(t, err)

f.Sheet["xl/worksheets/sheet1.xml"] = &xlsxWorksheet{
f.Sheet.Store("xl/worksheets/sheet1.xml", &xlsxWorksheet{
Dimension: &xlsxDimension{
Ref: "C2:C4",
},
}
})
_, err = f.Rows("Sheet1")
assert.NoError(t, err)
}
Expand Down Expand Up @@ -110,15 +110,15 @@ func TestGetColsError(t *testing.T) {
assert.EqualError(t, err, "sheet SheetN is not exist")

f = NewFile()
delete(f.Sheet, "xl/worksheets/sheet1.xml")
f.XLSX["xl/worksheets/sheet1.xml"] = []byte(`<worksheet><sheetData><row r="A"><c r="2" t="str"><v>B</v></c></row></sheetData></worksheet>`)
f.Sheet.Delete("xl/worksheets/sheet1.xml")
f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(`<worksheet><sheetData><row r="A"><c r="2" t="str"><v>B</v></c></row></sheetData></worksheet>`))
f.checked = nil
_, err = f.GetCols("Sheet1")
assert.EqualError(t, err, `strconv.Atoi: parsing "A": invalid syntax`)

f = NewFile()
delete(f.Sheet, "xl/worksheets/sheet1.xml")
f.XLSX["xl/worksheets/sheet1.xml"] = []byte(`<worksheet><sheetData><row r="2"><c r="A" t="str"><v>B</v></c></row></sheetData></worksheet>`)
f.Sheet.Delete("xl/worksheets/sheet1.xml")
f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(`<worksheet><sheetData><row r="2"><c r="A" t="str"><v>B</v></c></row></sheetData></worksheet>`))
f.checked = nil
_, err = f.GetCols("Sheet1")
assert.EqualError(t, err, `cannot convert cell "A" to coordinates: invalid cell name "A"`)
Expand All @@ -142,14 +142,14 @@ func TestColsRows(t *testing.T) {
assert.NoError(t, err)

assert.NoError(t, f.SetCellValue("Sheet1", "A1", 1))
f.Sheet["xl/worksheets/sheet1.xml"] = &xlsxWorksheet{
f.Sheet.Store("xl/worksheets/sheet1.xml", &xlsxWorksheet{
Dimension: &xlsxDimension{
Ref: "A1:A1",
},
}
})

f = NewFile()
f.XLSX["xl/worksheets/sheet1.xml"] = nil
f.Pkg.Store("xl/worksheets/sheet1.xml", nil)
_, err = f.Cols("Sheet1")
if !assert.NoError(t, err) {
t.FailNow()
Expand Down
22 changes: 11 additions & 11 deletions comment.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,11 +299,12 @@ func (f *File) addComment(commentsXML, cell string, formatSet *formatComment) {
// the folder xl.
func (f *File) countComments() int {
c1, c2 := 0, 0
for k := range f.XLSX {
if strings.Contains(k, "xl/comments") {
f.Pkg.Range(func(k, v interface{}) bool {
if strings.Contains(k.(string), "xl/comments") {
c1++
}
}
return true
})
for rel := range f.Comments {
if strings.Contains(rel, "xl/comments") {
c2++
Expand All @@ -321,10 +322,10 @@ func (f *File) decodeVMLDrawingReader(path string) *decodeVmlDrawing {
var err error

if f.DecodeVMLDrawing[path] == nil {
c, ok := f.XLSX[path]
if ok {
c, ok := f.Pkg.Load(path)
if ok && c != nil {
f.DecodeVMLDrawing[path] = new(decodeVmlDrawing)
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(c))).
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(c.([]byte)))).
Decode(f.DecodeVMLDrawing[path]); err != nil && err != io.EOF {
log.Printf("xml decode error: %s", err)
}
Expand All @@ -339,7 +340,7 @@ func (f *File) vmlDrawingWriter() {
for path, vml := range f.VMLDrawing {
if vml != nil {
v, _ := xml.Marshal(vml)
f.XLSX[path] = v
f.Pkg.Store(path, v)
}
}
}
Expand All @@ -348,12 +349,11 @@ func (f *File) vmlDrawingWriter() {
// after deserialization of xl/comments%d.xml.
func (f *File) commentsReader(path string) *xlsxComments {
var err error

if f.Comments[path] == nil {
content, ok := f.XLSX[path]
if ok {
content, ok := f.Pkg.Load(path)
if ok && content != nil {
f.Comments[path] = new(xlsxComments)
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(content))).
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(content.([]byte)))).
Decode(f.Comments[path]); err != nil && err != io.EOF {
log.Printf("xml decode error: %s", err)
}
Expand Down
Loading

0 comments on commit 544ef18

Please sign in to comment.