diff --git a/errors.go b/errors.go index 254890efa8e2deb0e5770face5af1d9b6a0b1306..a5d8814f53d72837e51808671edaa2c6968a9409 100644 --- a/errors.go +++ b/errors.go @@ -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 { diff --git a/table.go b/table.go index d59656daec723ef876e6177d1f88d2522a25f9b3..6aa1552eddabb01add10328f1f3e4bc19e777930 100644 --- a/table.go +++ b/table.go @@ -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 { diff --git a/table_test.go b/table_test.go index e6a67fb9a9c9e8a05c6fa009cc144d4ad9311b31..da4426586be21ee06ff568f93d4c6612dfd53d39 100644 --- a/table_test.go +++ b/table_test.go @@ -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) @@ -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("")) + assert.EqualError(t, f.DeleteTable("Table1"), ErrParameterInvalid.Error()) +} + func TestSetTableHeader(t *testing.T) { f := NewFile() _, err := f.setTableHeader("Sheet1", true, 1, 0, 1) diff --git a/xmlTable.go b/xmlTable.go index 789d4a287349d2ca26bb14ad24f23169d5079b92..00fa6748c91e00f9f710a67c83f84bacb880979f 100644 --- a/xmlTable.go +++ b/xmlTable.go @@ -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