Skip to content

Commit cb5a8e2

Browse files
authored
This closes qax-os#674, closes qax-os#1454, add new exported functions GetTables and DeleteTable (qax-os#1573)
1 parent 1b63d09 commit cb5a8e2

File tree

4 files changed

+138
-0
lines changed

4 files changed

+138
-0
lines changed

errors.go

+6
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,12 @@ func newNoExistSheetError(name string) error {
8282
return fmt.Errorf("sheet %s does not exist", name)
8383
}
8484

85+
// newNoExistTableError defined the error message on receiving the non existing
86+
// table name.
87+
func newNoExistTableError(name string) error {
88+
return fmt.Errorf("table %s does not exist", name)
89+
}
90+
8591
// newNotWorksheetError defined the error message on receiving a sheet which
8692
// not a worksheet.
8793
func newNotWorksheetError(name string) error {

table.go

+85
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,91 @@ func (f *File) AddTable(sheet string, table *Table) error {
125125
return f.addContentTypePart(tableID, "table")
126126
}
127127

128+
// GetTables provides the method to get all tables in a worksheet by given
129+
// worksheet name.
130+
func (f *File) GetTables(sheet string) ([]Table, error) {
131+
var tables []Table
132+
ws, err := f.workSheetReader(sheet)
133+
if err != nil {
134+
return tables, err
135+
}
136+
if ws.TableParts == nil {
137+
return tables, err
138+
}
139+
for _, tbl := range ws.TableParts.TableParts {
140+
if tbl != nil {
141+
target := f.getSheetRelationshipsTargetByID(sheet, tbl.RID)
142+
tableXML := strings.ReplaceAll(target, "..", "xl")
143+
content, ok := f.Pkg.Load(tableXML)
144+
if !ok {
145+
continue
146+
}
147+
var t xlsxTable
148+
if err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(content.([]byte)))).
149+
Decode(&t); err != nil && err != io.EOF {
150+
return tables, err
151+
}
152+
table := Table{
153+
rID: tbl.RID,
154+
Range: t.Ref,
155+
Name: t.Name,
156+
}
157+
if t.TableStyleInfo != nil {
158+
table.StyleName = t.TableStyleInfo.Name
159+
table.ShowColumnStripes = t.TableStyleInfo.ShowColumnStripes
160+
table.ShowFirstColumn = t.TableStyleInfo.ShowFirstColumn
161+
table.ShowLastColumn = t.TableStyleInfo.ShowLastColumn
162+
table.ShowRowStripes = &t.TableStyleInfo.ShowRowStripes
163+
}
164+
tables = append(tables, table)
165+
}
166+
}
167+
return tables, err
168+
}
169+
170+
// DeleteTable provides the method to delete table by given table name.
171+
func (f *File) DeleteTable(name string) error {
172+
if err := checkDefinedName(name); err != nil {
173+
return err
174+
}
175+
for _, sheet := range f.GetSheetList() {
176+
tables, err := f.GetTables(sheet)
177+
if err != nil {
178+
return err
179+
}
180+
for _, table := range tables {
181+
if table.Name != name {
182+
continue
183+
}
184+
ws, _ := f.workSheetReader(sheet)
185+
for i, tbl := range ws.TableParts.TableParts {
186+
if tbl.RID == table.rID {
187+
ws.TableParts.TableParts = append(ws.TableParts.TableParts[:i], ws.TableParts.TableParts[i+1:]...)
188+
f.deleteSheetRelationships(sheet, tbl.RID)
189+
break
190+
}
191+
}
192+
if ws.TableParts.Count = len(ws.TableParts.TableParts); ws.TableParts.Count == 0 {
193+
ws.TableParts = nil
194+
}
195+
// Delete cell value in the table header
196+
coordinates, err := rangeRefToCoordinates(table.Range)
197+
if err != nil {
198+
return err
199+
}
200+
_ = sortCoordinates(coordinates)
201+
for col := coordinates[0]; col <= coordinates[2]; col++ {
202+
for row := coordinates[1]; row < coordinates[1]+1; row++ {
203+
cell, _ := CoordinatesToCellName(col, row)
204+
err = f.SetCellValue(sheet, cell, nil)
205+
}
206+
}
207+
return err
208+
}
209+
}
210+
return newNoExistTableError(name)
211+
}
212+
128213
// countTables provides a function to get table files count storage in the
129214
// folder xl/tables.
130215
func (f *File) countTables() int {

table_test.go

+46
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ func TestAddTable(t *testing.T) {
2727
ShowHeaderRow: boolPtr(false),
2828
}))
2929
assert.NoError(t, f.AddTable("Sheet2", &Table{Range: "F1:F1", StyleName: "TableStyleMedium8"}))
30+
// Test get tables in worksheet
31+
tables, err := f.GetTables("Sheet2")
32+
assert.Len(t, tables, 3)
33+
assert.NoError(t, err)
3034

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

81+
func TestGetTables(t *testing.T) {
82+
f := NewFile()
83+
// Test get tables in none table worksheet
84+
tables, err := f.GetTables("Sheet1")
85+
assert.Len(t, tables, 0)
86+
assert.NoError(t, err)
87+
// Test get tables in not exist worksheet
88+
_, err = f.GetTables("SheetN")
89+
assert.EqualError(t, err, "sheet SheetN does not exist")
90+
// Test adjust table with unsupported charset
91+
assert.NoError(t, f.AddTable("Sheet1", &Table{Range: "B26:A21"}))
92+
f.Pkg.Store("xl/tables/table1.xml", MacintoshCyrillicCharset)
93+
_, err = f.GetTables("Sheet1")
94+
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
95+
// Test adjust table with no exist table parts
96+
f.Pkg.Delete("xl/tables/table1.xml")
97+
tables, err = f.GetTables("Sheet1")
98+
assert.Len(t, tables, 0)
99+
assert.NoError(t, err)
100+
}
101+
102+
func TestDeleteTable(t *testing.T) {
103+
f := NewFile()
104+
assert.NoError(t, f.AddTable("Sheet1", &Table{Range: "A1:B4", Name: "Table1"}))
105+
assert.NoError(t, f.AddTable("Sheet1", &Table{Range: "B26:A21", Name: "Table2"}))
106+
assert.NoError(t, f.DeleteTable("Table2"))
107+
assert.NoError(t, f.DeleteTable("Table1"))
108+
// Test delete table with invalid table name
109+
assert.EqualError(t, f.DeleteTable("Table 1"), newInvalidNameError("Table 1").Error())
110+
// Test delete table with no exist table name
111+
assert.EqualError(t, f.DeleteTable("Table"), newNoExistTableError("Table").Error())
112+
// Test delete table with unsupported charset
113+
f.Sheet.Delete("xl/worksheets/sheet1.xml")
114+
f.Pkg.Store("xl/worksheets/sheet1.xml", MacintoshCyrillicCharset)
115+
assert.EqualError(t, f.DeleteTable("Table1"), "XML syntax error on line 1: invalid UTF-8")
116+
// Test delete table with invalid table range
117+
f = NewFile()
118+
assert.NoError(t, f.AddTable("Sheet1", &Table{Range: "A1:B4", Name: "Table1"}))
119+
f.Pkg.Store("xl/tables/table1.xml", []byte("<table name=\"Table1\" ref=\"-\" />"))
120+
assert.EqualError(t, f.DeleteTable("Table1"), ErrParameterInvalid.Error())
121+
}
122+
77123
func TestSetTableHeader(t *testing.T) {
78124
f := NewFile()
79125
_, err := f.setTableHeader("Sheet1", true, 1, 0, 1)

xmlTable.go

+1
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ type xlsxTableStyleInfo struct {
198198

199199
// Table directly maps the format settings of the table.
200200
type Table struct {
201+
rID string
201202
Range string
202203
Name string
203204
StyleName string

0 commit comments

Comments
 (0)