Skip to content

Commit

Permalink
This closes #674, closes #1454, add new exported functions GetTables …
Browse files Browse the repository at this point in the history
…and DeleteTable (#1573)
  • Loading branch information
fsfsx authored Aug 23, 2023
1 parent 1b63d09 commit cb5a8e2
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 0 deletions.
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) {
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 {
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

0 comments on commit cb5a8e2

Please sign in to comment.