Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

This closes #674, closes #1454, add new exported functions GetTables and DeleteTable #1573

Merged
merged 11 commits into from
Aug 23, 2023
6 changes: 6 additions & 0 deletions errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ func newNoExistSheetError(name string) error {
return fmt.Errorf("sheet %s does not exist", name)
}

// newNoExistTableError defined the error message on receiving the non existing
// table name.
func newNoExistTableError(name string) error {
return fmt.Errorf("table %s does not exist", name)
}

// newNotWorksheetError defined the error message on receiving a sheet which
// not a worksheet.
func newNotWorksheetError(name string) error {
Expand Down
85 changes: 85 additions & 0 deletions table.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,91 @@ func (f *File) AddTable(sheet string, table *Table) error {
return f.addContentTypePart(tableID, "table")
}

// GetTables provides the method to get all tables in a worksheet by given
// worksheet name.
func (f *File) GetTables(sheet string) ([]Table, error) {
fsfsx marked this conversation as resolved.
Show resolved Hide resolved
var tables []Table
ws, err := f.workSheetReader(sheet)
if err != nil {
return tables, err
}
if ws.TableParts == nil {
return tables, err
}
for _, tbl := range ws.TableParts.TableParts {
fsfsx marked this conversation as resolved.
Show resolved Hide resolved
if tbl != nil {
target := f.getSheetRelationshipsTargetByID(sheet, tbl.RID)
tableXML := strings.ReplaceAll(target, "..", "xl")
content, ok := f.Pkg.Load(tableXML)
if !ok {
continue
}
var t xlsxTable
if err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(content.([]byte)))).
Decode(&t); err != nil && err != io.EOF {
return tables, err
}
table := Table{
rID: tbl.RID,
Range: t.Ref,
Name: t.Name,
}
if t.TableStyleInfo != nil {
table.StyleName = t.TableStyleInfo.Name
table.ShowColumnStripes = t.TableStyleInfo.ShowColumnStripes
table.ShowFirstColumn = t.TableStyleInfo.ShowFirstColumn
table.ShowLastColumn = t.TableStyleInfo.ShowLastColumn
table.ShowRowStripes = &t.TableStyleInfo.ShowRowStripes
}
tables = append(tables, table)
}
}
return tables, err
}

// DeleteTable provides the method to delete table by given table name.
func (f *File) DeleteTable(name string) error {
if err := checkDefinedName(name); err != nil {
return err
}
for _, sheet := range f.GetSheetList() {
tables, err := f.GetTables(sheet)
if err != nil {
return err
}
for _, table := range tables {
if table.Name != name {
continue
}
ws, _ := f.workSheetReader(sheet)
for i, tbl := range ws.TableParts.TableParts {
if tbl.RID == table.rID {
ws.TableParts.TableParts = append(ws.TableParts.TableParts[:i], ws.TableParts.TableParts[i+1:]...)
f.deleteSheetRelationships(sheet, tbl.RID)
break
}
}
if ws.TableParts.Count = len(ws.TableParts.TableParts); ws.TableParts.Count == 0 {
ws.TableParts = nil
}
// Delete cell value in the table header
coordinates, err := rangeRefToCoordinates(table.Range)
if err != nil {
return err
}
_ = sortCoordinates(coordinates)
for col := coordinates[0]; col <= coordinates[2]; col++ {
for row := coordinates[1]; row < coordinates[1]+1; row++ {
cell, _ := CoordinatesToCellName(col, row)
err = f.SetCellValue(sheet, cell, nil)
}
}
return err
}
}
return newNoExistTableError(name)
}

// countTables provides a function to get table files count storage in the
// folder xl/tables.
func (f *File) countTables() int {
Expand Down
46 changes: 46 additions & 0 deletions table_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ func TestAddTable(t *testing.T) {
ShowHeaderRow: boolPtr(false),
}))
assert.NoError(t, f.AddTable("Sheet2", &Table{Range: "F1:F1", StyleName: "TableStyleMedium8"}))
// Test get tables in worksheet
tables, err := f.GetTables("Sheet2")
assert.Len(t, tables, 3)
assert.NoError(t, err)

// Test add table with already exist table name
assert.Equal(t, f.AddTable("Sheet2", &Table{Name: "Table1"}), ErrExistsTableName)
Expand Down Expand Up @@ -74,6 +78,48 @@ func TestAddTable(t *testing.T) {
assert.NoError(t, f.AddTable("Sheet1", &Table{Range: "A1:B2"}))
}

func TestGetTables(t *testing.T) {
f := NewFile()
// Test get tables in none table worksheet
tables, err := f.GetTables("Sheet1")
assert.Len(t, tables, 0)
assert.NoError(t, err)
// Test get tables in not exist worksheet
_, err = f.GetTables("SheetN")
assert.EqualError(t, err, "sheet SheetN does not exist")
// Test adjust table with unsupported charset
assert.NoError(t, f.AddTable("Sheet1", &Table{Range: "B26:A21"}))
f.Pkg.Store("xl/tables/table1.xml", MacintoshCyrillicCharset)
_, err = f.GetTables("Sheet1")
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
// Test adjust table with no exist table parts
f.Pkg.Delete("xl/tables/table1.xml")
tables, err = f.GetTables("Sheet1")
assert.Len(t, tables, 0)
assert.NoError(t, err)
}

func TestDeleteTable(t *testing.T) {
f := NewFile()
assert.NoError(t, f.AddTable("Sheet1", &Table{Range: "A1:B4", Name: "Table1"}))
assert.NoError(t, f.AddTable("Sheet1", &Table{Range: "B26:A21", Name: "Table2"}))
assert.NoError(t, f.DeleteTable("Table2"))
assert.NoError(t, f.DeleteTable("Table1"))
// Test delete table with invalid table name
assert.EqualError(t, f.DeleteTable("Table 1"), newInvalidNameError("Table 1").Error())
// Test delete table with no exist table name
assert.EqualError(t, f.DeleteTable("Table"), newNoExistTableError("Table").Error())
// Test delete table with unsupported charset
f.Sheet.Delete("xl/worksheets/sheet1.xml")
f.Pkg.Store("xl/worksheets/sheet1.xml", MacintoshCyrillicCharset)
assert.EqualError(t, f.DeleteTable("Table1"), "XML syntax error on line 1: invalid UTF-8")
// Test delete table with invalid table range
f = NewFile()
assert.NoError(t, f.AddTable("Sheet1", &Table{Range: "A1:B4", Name: "Table1"}))
f.Pkg.Store("xl/tables/table1.xml", []byte("<table name=\"Table1\" ref=\"-\" />"))
assert.EqualError(t, f.DeleteTable("Table1"), ErrParameterInvalid.Error())
}

func TestSetTableHeader(t *testing.T) {
f := NewFile()
_, err := f.setTableHeader("Sheet1", true, 1, 0, 1)
Expand Down
1 change: 1 addition & 0 deletions xmlTable.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ type xlsxTableStyleInfo struct {

// Table directly maps the format settings of the table.
type Table struct {
rID string
Range string
Name string
StyleName string
Expand Down