- Update unit tests
This commit is contained in:
parent
9a38657515
commit
c805be1f6f
|
@ -264,6 +264,12 @@ func newInvalidStyleID(styleID int) error {
|
||||||
return fmt.Errorf("invalid style ID %d", styleID)
|
return fmt.Errorf("invalid style ID %d", styleID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// newNoExistSlicerError defined the error message on receiving the non existing
|
||||||
|
// slicer name.
|
||||||
|
func newNoExistSlicerError(name string) error {
|
||||||
|
return fmt.Errorf("slicer %s does not exist", name)
|
||||||
|
}
|
||||||
|
|
||||||
// newNoExistTableError defined the error message on receiving the non existing
|
// newNoExistTableError defined the error message on receiving the non existing
|
||||||
// table name.
|
// table name.
|
||||||
func newNoExistTableError(name string) error {
|
func newNoExistTableError(name string) error {
|
||||||
|
|
|
@ -785,12 +785,11 @@ func (f *File) getPivotTableDataRange(opts *PivotTableOptions) error {
|
||||||
opts.pivotDataRange = opts.DataRange
|
opts.pivotDataRange = opts.DataRange
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
for _, sheetName := range f.GetSheetList() {
|
tbls, err := f.getTables()
|
||||||
tables, err := f.GetTables(sheetName)
|
if err != nil {
|
||||||
e := ErrSheetNotExist{sheetName}
|
return err
|
||||||
if err != nil && err.Error() != newNotWorksheetError(sheetName).Error() && err.Error() != e.Error() {
|
}
|
||||||
return err
|
for sheetName, tables := range tbls {
|
||||||
}
|
|
||||||
for _, table := range tables {
|
for _, table := range tables {
|
||||||
if table.Name == opts.DataRange {
|
if table.Name == opts.DataRange {
|
||||||
opts.pivotDataRange, opts.namedDataRange = fmt.Sprintf("%s!%s", sheetName, table.Range), true
|
opts.pivotDataRange, opts.namedDataRange = fmt.Sprintf("%s!%s", sheetName, table.Range), true
|
||||||
|
@ -1016,8 +1015,8 @@ func (f *File) DeletePivotTable(sheet, name string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
pivotTableCaches := map[string]int{}
|
pivotTableCaches := map[string]int{}
|
||||||
for _, sheetName := range f.GetSheetList() {
|
pivotTables, _ := f.getPivotTables()
|
||||||
sheetPivotTables, _ := f.GetPivotTables(sheetName)
|
for _, sheetPivotTables := range pivotTables {
|
||||||
for _, sheetPivotTable := range sheetPivotTables {
|
for _, sheetPivotTable := range sheetPivotTables {
|
||||||
pivotTableCaches[sheetPivotTable.pivotCacheXML]++
|
pivotTableCaches[sheetPivotTable.pivotCacheXML]++
|
||||||
}
|
}
|
||||||
|
@ -1038,3 +1037,17 @@ func (f *File) DeletePivotTable(sheet, name string) error {
|
||||||
}
|
}
|
||||||
return newNoExistTableError(name)
|
return newNoExistTableError(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getPivotTables provides a function to get all pivot tables in a workbook.
|
||||||
|
func (f *File) getPivotTables() (map[string][]PivotTableOptions, error) {
|
||||||
|
pivotTables := map[string][]PivotTableOptions{}
|
||||||
|
for _, sheetName := range f.GetSheetList() {
|
||||||
|
pts, err := f.GetPivotTables(sheetName)
|
||||||
|
e := ErrSheetNotExist{sheetName}
|
||||||
|
if err != nil && err.Error() != newNotWorksheetError(sheetName).Error() && err.Error() != e.Error() {
|
||||||
|
return pivotTables, err
|
||||||
|
}
|
||||||
|
pivotTables[sheetName] = append(pivotTables[sheetName], pts...)
|
||||||
|
}
|
||||||
|
return pivotTables, nil
|
||||||
|
}
|
||||||
|
|
|
@ -343,6 +343,8 @@ func TestPivotTable(t *testing.T) {
|
||||||
f.Pkg.Store("xl/pivotTables/pivotTable1.xml", MacintoshCyrillicCharset)
|
f.Pkg.Store("xl/pivotTables/pivotTable1.xml", MacintoshCyrillicCharset)
|
||||||
_, err = f.GetPivotTables("Sheet1")
|
_, err = f.GetPivotTables("Sheet1")
|
||||||
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
|
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
|
||||||
|
_, err = f.getPivotTables()
|
||||||
|
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
|
||||||
assert.NoError(t, f.Close())
|
assert.NoError(t, f.Close())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
357
slicer.go
357
slicer.go
|
@ -53,17 +53,23 @@ import (
|
||||||
//
|
//
|
||||||
// Format specifies the format of the slicer, this setting is optional.
|
// Format specifies the format of the slicer, this setting is optional.
|
||||||
type SlicerOptions struct {
|
type SlicerOptions struct {
|
||||||
Name string
|
slicerXML string
|
||||||
Cell string
|
slicerCacheXML string
|
||||||
TableSheet string
|
slicerCacheName string
|
||||||
TableName string
|
slicerSheetName string
|
||||||
Caption string
|
slicerSheetRID string
|
||||||
Macro string
|
drawingXML string
|
||||||
Width uint
|
Name string
|
||||||
Height uint
|
Cell string
|
||||||
DisplayHeader *bool
|
TableSheet string
|
||||||
ItemDesc bool
|
TableName string
|
||||||
Format GraphicOptions
|
Caption string
|
||||||
|
Macro string
|
||||||
|
Width uint
|
||||||
|
Height uint
|
||||||
|
DisplayHeader *bool
|
||||||
|
ItemDesc bool
|
||||||
|
Format GraphicOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddSlicer function inserts a slicer by giving the worksheet name and slicer
|
// AddSlicer function inserts a slicer by giving the worksheet name and slicer
|
||||||
|
@ -99,7 +105,7 @@ func (f *File) AddSlicer(sheet string, opts *SlicerOptions) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
slicerCacheName, err := f.setSlicerCache(sheet, colIdx, opts, table, pivotTable)
|
slicerCacheName, err := f.setSlicerCache(colIdx, opts, table, pivotTable)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -224,7 +230,6 @@ func (f *File) addSheetSlicer(sheet, extURI string) (int, error) {
|
||||||
slicerID = f.countSlicers() + 1
|
slicerID = f.countSlicers() + 1
|
||||||
ws, err = f.workSheetReader(sheet)
|
ws, err = f.workSheetReader(sheet)
|
||||||
decodeExtLst = new(decodeExtLst)
|
decodeExtLst = new(decodeExtLst)
|
||||||
slicerList = new(decodeSlicerList)
|
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return slicerID, err
|
return slicerID, err
|
||||||
|
@ -236,6 +241,7 @@ func (f *File) addSheetSlicer(sheet, extURI string) (int, error) {
|
||||||
}
|
}
|
||||||
for _, ext := range decodeExtLst.Ext {
|
for _, ext := range decodeExtLst.Ext {
|
||||||
if ext.URI == extURI {
|
if ext.URI == extURI {
|
||||||
|
slicerList := new(decodeSlicerList)
|
||||||
_ = f.xmlNewDecoder(strings.NewReader(ext.Content)).Decode(slicerList)
|
_ = f.xmlNewDecoder(strings.NewReader(ext.Content)).Decode(slicerList)
|
||||||
for _, slicer := range slicerList.Slicer {
|
for _, slicer := range slicerList.Slicer {
|
||||||
if slicer.RID != "" {
|
if slicer.RID != "" {
|
||||||
|
@ -390,14 +396,13 @@ func (f *File) genSlicerCacheName(name string) string {
|
||||||
// setSlicerCache check if a slicer cache already exists or add a new slicer
|
// setSlicerCache check if a slicer cache already exists or add a new slicer
|
||||||
// cache by giving the column index, slicer, table options, and returns the
|
// cache by giving the column index, slicer, table options, and returns the
|
||||||
// slicer cache name.
|
// slicer cache name.
|
||||||
func (f *File) setSlicerCache(sheet string, colIdx int, opts *SlicerOptions, table *Table, pivotTable *PivotTableOptions) (string, error) {
|
func (f *File) setSlicerCache(colIdx int, opts *SlicerOptions, table *Table, pivotTable *PivotTableOptions) (string, error) {
|
||||||
var ok bool
|
var ok bool
|
||||||
var slicerCacheName string
|
var slicerCacheName string
|
||||||
f.Pkg.Range(func(k, v interface{}) bool {
|
f.Pkg.Range(func(k, v interface{}) bool {
|
||||||
if strings.Contains(k.(string), "xl/slicerCaches/slicerCache") {
|
if strings.Contains(k.(string), "xl/slicerCaches/slicerCache") {
|
||||||
slicerCache := &xlsxSlicerCacheDefinition{}
|
slicerCache, err := f.slicerCacheReader(k.(string))
|
||||||
if err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(v.([]byte)))).
|
if err != nil {
|
||||||
Decode(slicerCache); err != nil && err != io.EOF {
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if pivotTable != nil && slicerCache.PivotTables != nil {
|
if pivotTable != nil && slicerCache.PivotTables != nil {
|
||||||
|
@ -449,6 +454,20 @@ func (f *File) slicerReader(slicerXML string) (*xlsxSlicers, error) {
|
||||||
return slicer, nil
|
return slicer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// slicerCacheReader provides a function to get the pointer to the structure
|
||||||
|
// after deserialization of xl/slicerCaches/slicerCache%d.xml.
|
||||||
|
func (f *File) slicerCacheReader(slicerCacheXML string) (*xlsxSlicerCacheDefinition, error) {
|
||||||
|
content, ok := f.Pkg.Load(slicerCacheXML)
|
||||||
|
slicerCache := &xlsxSlicerCacheDefinition{}
|
||||||
|
if ok && content != nil {
|
||||||
|
if err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(content.([]byte)))).
|
||||||
|
Decode(slicerCache); err != nil && err != io.EOF {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return slicerCache, nil
|
||||||
|
}
|
||||||
|
|
||||||
// timelineReader provides a function to get the pointer to the structure
|
// timelineReader provides a function to get the pointer to the structure
|
||||||
// after deserialization of xl/timelines/timeline%d.xml.
|
// after deserialization of xl/timelines/timeline%d.xml.
|
||||||
func (f *File) timelineReader(timelineXML string) (*xlsxTimelines, error) {
|
func (f *File) timelineReader(timelineXML string) (*xlsxTimelines, error) {
|
||||||
|
@ -586,6 +605,7 @@ func (f *File) addDrawingSlicer(sheet, slicerName string, ns xml.Attr, opts *Sli
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
graphicFrame := xlsxGraphicFrame{
|
graphicFrame := xlsxGraphicFrame{
|
||||||
|
Macro: opts.Macro,
|
||||||
NvGraphicFramePr: xlsxNvGraphicFramePr{
|
NvGraphicFramePr: xlsxNvGraphicFramePr{
|
||||||
CNvPr: &xlsxCNvPr{
|
CNvPr: &xlsxCNvPr{
|
||||||
ID: cNvPrID,
|
ID: cNvPrID,
|
||||||
|
@ -725,3 +745,306 @@ func (f *File) addWorkbookSlicerCache(slicerCacheID int, URI string) error {
|
||||||
wb.ExtLst = &xlsxExtLst{Ext: strings.TrimSuffix(strings.TrimPrefix(string(extLstBytes), "<extLst>"), "</extLst>")}
|
wb.ExtLst = &xlsxExtLst{Ext: strings.TrimSuffix(strings.TrimPrefix(string(extLstBytes), "<extLst>"), "</extLst>")}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetSlicers provides the method to get all slicers in a worksheet by a given
|
||||||
|
// worksheet name. Note that, this function does not support getting the height,
|
||||||
|
// width, and graphic options of the slicer shape currently.
|
||||||
|
func (f *File) GetSlicers(sheet string) ([]SlicerOptions, error) {
|
||||||
|
var (
|
||||||
|
slicers []SlicerOptions
|
||||||
|
ws, err = f.workSheetReader(sheet)
|
||||||
|
decodeExtLst = new(decodeExtLst)
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return slicers, err
|
||||||
|
}
|
||||||
|
if ws.ExtLst == nil {
|
||||||
|
return slicers, err
|
||||||
|
}
|
||||||
|
target := f.getSheetRelationshipsTargetByID(sheet, ws.Drawing.RID)
|
||||||
|
drawingXML := strings.TrimPrefix(strings.ReplaceAll(target, "..", "xl"), "/")
|
||||||
|
if err = f.xmlNewDecoder(strings.NewReader("<extLst>" + ws.ExtLst.Ext + "</extLst>")).
|
||||||
|
Decode(decodeExtLst); err != nil && err != io.EOF {
|
||||||
|
return slicers, err
|
||||||
|
}
|
||||||
|
for _, ext := range decodeExtLst.Ext {
|
||||||
|
if ext.URI == ExtURISlicerListX14 || ext.URI == ExtURISlicerListX15 {
|
||||||
|
slicerList := new(decodeSlicerList)
|
||||||
|
_ = f.xmlNewDecoder(strings.NewReader(ext.Content)).Decode(&slicerList)
|
||||||
|
for _, slicer := range slicerList.Slicer {
|
||||||
|
if slicer.RID != "" {
|
||||||
|
opts, err := f.getSlicers(sheet, slicer.RID, drawingXML)
|
||||||
|
if err != nil {
|
||||||
|
return slicers, err
|
||||||
|
}
|
||||||
|
slicers = append(slicers, opts...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return slicers, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// getSlicerCache provides a function to get a slicer cache by given slicer
|
||||||
|
// cache name and slicer options.
|
||||||
|
func (f *File) getSlicerCache(slicerCacheName string, opt *SlicerOptions) *xlsxSlicerCacheDefinition {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
slicerCache *xlsxSlicerCacheDefinition
|
||||||
|
)
|
||||||
|
f.Pkg.Range(func(k, v interface{}) bool {
|
||||||
|
if strings.Contains(k.(string), "xl/slicerCaches/slicerCache") {
|
||||||
|
slicerCache, err = f.slicerCacheReader(k.(string))
|
||||||
|
if err != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if slicerCache.Name == slicerCacheName {
|
||||||
|
opt.slicerCacheXML = k.(string)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
return slicerCache
|
||||||
|
}
|
||||||
|
|
||||||
|
// getSlicers provides a function to get slicers options by given worksheet
|
||||||
|
// name, slicer part relationship ID and drawing part path.
|
||||||
|
func (f *File) getSlicers(sheet, rID, drawingXML string) ([]SlicerOptions, error) {
|
||||||
|
var (
|
||||||
|
opts []SlicerOptions
|
||||||
|
sheetRelationshipsSlicerXML = f.getSheetRelationshipsTargetByID(sheet, rID)
|
||||||
|
slicerXML = strings.ReplaceAll(sheetRelationshipsSlicerXML, "..", "xl")
|
||||||
|
slicers, err = f.slicerReader(slicerXML)
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return opts, err
|
||||||
|
}
|
||||||
|
for _, slicer := range slicers.Slicer {
|
||||||
|
opt := SlicerOptions{
|
||||||
|
slicerXML: slicerXML,
|
||||||
|
slicerCacheName: slicer.Cache,
|
||||||
|
slicerSheetName: sheet,
|
||||||
|
slicerSheetRID: rID,
|
||||||
|
drawingXML: drawingXML,
|
||||||
|
Name: slicer.Name,
|
||||||
|
Caption: slicer.Caption,
|
||||||
|
DisplayHeader: slicer.ShowCaption,
|
||||||
|
}
|
||||||
|
slicerCache := f.getSlicerCache(slicer.Cache, &opt)
|
||||||
|
if slicerCache == nil {
|
||||||
|
return opts, err
|
||||||
|
}
|
||||||
|
if err := f.extractTableSlicer(slicerCache, &opt); err != nil {
|
||||||
|
return opts, err
|
||||||
|
}
|
||||||
|
if err := f.extractPivotTableSlicer(slicerCache, &opt); err != nil {
|
||||||
|
return opts, err
|
||||||
|
}
|
||||||
|
if err = f.extractSlicerCellAnchor(drawingXML, &opt); err != nil {
|
||||||
|
return opts, err
|
||||||
|
}
|
||||||
|
opts = append(opts, opt)
|
||||||
|
}
|
||||||
|
return opts, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// extractTableSlicer extract table slicer options from slicer cache.
|
||||||
|
func (f *File) extractTableSlicer(slicerCache *xlsxSlicerCacheDefinition, opt *SlicerOptions) error {
|
||||||
|
if slicerCache.ExtLst != nil {
|
||||||
|
tables, err := f.getTables()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ext := new(xlsxExt)
|
||||||
|
_ = f.xmlNewDecoder(strings.NewReader(slicerCache.ExtLst.Ext)).Decode(ext)
|
||||||
|
if ext.URI == ExtURISlicerCacheDefinition {
|
||||||
|
tableSlicerCache := new(decodeTableSlicerCache)
|
||||||
|
_ = f.xmlNewDecoder(strings.NewReader(ext.Content)).Decode(tableSlicerCache)
|
||||||
|
opt.ItemDesc = tableSlicerCache.SortOrder == "descending"
|
||||||
|
for sheetName, sheetTables := range tables {
|
||||||
|
for _, table := range sheetTables {
|
||||||
|
if tableSlicerCache.TableID == table.tID {
|
||||||
|
opt.TableName = table.Name
|
||||||
|
opt.TableSheet = sheetName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// extractPivotTableSlicer extract pivot table slicer options from slicer cache.
|
||||||
|
func (f *File) extractPivotTableSlicer(slicerCache *xlsxSlicerCacheDefinition, opt *SlicerOptions) error {
|
||||||
|
pivotTables, err := f.getPivotTables()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if slicerCache.PivotTables != nil {
|
||||||
|
for _, pt := range slicerCache.PivotTables.PivotTable {
|
||||||
|
opt.TableName = pt.Name
|
||||||
|
for sheetName, sheetPivotTables := range pivotTables {
|
||||||
|
for _, pivotTable := range sheetPivotTables {
|
||||||
|
if opt.TableName == pivotTable.Name {
|
||||||
|
opt.TableSheet = sheetName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if slicerCache.Data != nil && slicerCache.Data.Tabular != nil {
|
||||||
|
opt.ItemDesc = slicerCache.Data.Tabular.SortOrder == "descending"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// extractSlicerCellAnchor extract slicer drawing object from two cell anchor by
|
||||||
|
// giving drawing part path and slicer options.
|
||||||
|
func (f *File) extractSlicerCellAnchor(drawingXML string, opt *SlicerOptions) error {
|
||||||
|
var (
|
||||||
|
wsDr *xlsxWsDr
|
||||||
|
deCellAnchor = new(decodeCellAnchor)
|
||||||
|
deChoice = new(decodeChoice)
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if wsDr, _, err = f.drawingParser(drawingXML); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
wsDr.mu.Lock()
|
||||||
|
defer wsDr.mu.Unlock()
|
||||||
|
cond := func(ac *xlsxAlternateContent) bool {
|
||||||
|
if ac != nil {
|
||||||
|
_ = f.xmlNewDecoder(strings.NewReader(ac.Content)).Decode(&deChoice)
|
||||||
|
if deChoice.XMLNSSle15 == NameSpaceDrawingMLSlicerX15.Value || deChoice.XMLNSA14 == NameSpaceDrawingMLA14.Value {
|
||||||
|
if deChoice.GraphicFrame.NvGraphicFramePr.CNvPr.Name == opt.Name {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, anchor := range wsDr.TwoCellAnchor {
|
||||||
|
for _, ac := range anchor.AlternateContent {
|
||||||
|
if cond(ac) {
|
||||||
|
if anchor.From != nil {
|
||||||
|
opt.Macro = deChoice.GraphicFrame.Macro
|
||||||
|
if opt.Cell, err = CoordinatesToCellName(anchor.From.Col+1, anchor.From.Row+1); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ = f.xmlNewDecoder(strings.NewReader("<decodeCellAnchor>" + anchor.GraphicFrame + "</decodeCellAnchor>")).Decode(&deCellAnchor)
|
||||||
|
for _, ac := range deCellAnchor.AlternateContent {
|
||||||
|
if cond(ac) {
|
||||||
|
if deCellAnchor.From != nil {
|
||||||
|
opt.Macro = deChoice.GraphicFrame.Macro
|
||||||
|
if opt.Cell, err = CoordinatesToCellName(deCellAnchor.From.Col+1, deCellAnchor.From.Row+1); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// getAllSlicers provides a function to get all slicers in a workbook.
|
||||||
|
func (f *File) getAllSlicers() (map[string][]SlicerOptions, error) {
|
||||||
|
slicers := map[string][]SlicerOptions{}
|
||||||
|
for _, sheetName := range f.GetSheetList() {
|
||||||
|
sles, err := f.GetSlicers(sheetName)
|
||||||
|
e := ErrSheetNotExist{sheetName}
|
||||||
|
if err != nil && err.Error() != newNotWorksheetError(sheetName).Error() && err.Error() != e.Error() {
|
||||||
|
return slicers, err
|
||||||
|
}
|
||||||
|
slicers[sheetName] = append(slicers[sheetName], sles...)
|
||||||
|
}
|
||||||
|
return slicers, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteSlicer provides the method to delete a slicer by a given slicer name.
|
||||||
|
func (f *File) DeleteSlicer(name string) error {
|
||||||
|
sles, err := f.getAllSlicers()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, slicers := range sles {
|
||||||
|
for _, slicer := range slicers {
|
||||||
|
if slicer.Name != name {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
_ = f.deleteSlicer(slicer)
|
||||||
|
return f.deleteSlicerCache(sles, slicer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newNoExistSlicerError(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getSlicers provides a function to delete slicer by given slicer options.
|
||||||
|
func (f *File) deleteSlicer(opts SlicerOptions) error {
|
||||||
|
slicers, err := f.slicerReader(opts.slicerXML)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for i := 0; i < len(slicers.Slicer); i++ {
|
||||||
|
if slicers.Slicer[i].Name == opts.Name {
|
||||||
|
slicers.Slicer = append(slicers.Slicer[:i], slicers.Slicer[i+1:]...)
|
||||||
|
i--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(slicers.Slicer) == 0 {
|
||||||
|
var (
|
||||||
|
extLstBytes []byte
|
||||||
|
ws, err = f.workSheetReader(opts.slicerSheetName)
|
||||||
|
decodeExtLst = new(decodeExtLst)
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = f.xmlNewDecoder(strings.NewReader("<extLst>" + ws.ExtLst.Ext + "</extLst>")).
|
||||||
|
Decode(decodeExtLst); err != nil && err != io.EOF {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for i, ext := range decodeExtLst.Ext {
|
||||||
|
if ext.URI == ExtURISlicerListX14 || ext.URI == ExtURISlicerListX15 {
|
||||||
|
slicerList := new(decodeSlicerList)
|
||||||
|
_ = f.xmlNewDecoder(strings.NewReader(ext.Content)).Decode(slicerList)
|
||||||
|
for _, slicer := range slicerList.Slicer {
|
||||||
|
if slicer.RID == opts.slicerSheetRID {
|
||||||
|
decodeExtLst.Ext = append(decodeExtLst.Ext[:i], decodeExtLst.Ext[i+1:]...)
|
||||||
|
extLstBytes, err = xml.Marshal(decodeExtLst)
|
||||||
|
ws.ExtLst = &xlsxExtLst{Ext: strings.TrimSuffix(strings.TrimPrefix(string(extLstBytes), "<extLst>"), "</extLst>")}
|
||||||
|
f.Pkg.Delete(opts.slicerXML)
|
||||||
|
_ = f.removeContentTypesPart(ContentTypeSlicer, "/"+opts.slicerXML)
|
||||||
|
f.deleteSheetRelationships(opts.slicerSheetName, opts.slicerSheetRID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
output, err := xml.Marshal(slicers)
|
||||||
|
f.saveFileList(opts.slicerXML, output)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// deleteSlicerCache provides a function to delete the slicer cache by giving
|
||||||
|
// slicer options if the slicer cache is no longer used.
|
||||||
|
func (f *File) deleteSlicerCache(sles map[string][]SlicerOptions, opts SlicerOptions) error {
|
||||||
|
for _, slicers := range sles {
|
||||||
|
for _, slicer := range slicers {
|
||||||
|
if slicer.Name != opts.Name && slicer.slicerCacheName == opts.slicerCacheName {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := f.DeleteDefinedName(&DefinedName{Name: opts.slicerCacheName}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
f.Pkg.Delete(opts.slicerCacheXML)
|
||||||
|
return f.removeContentTypesPart(ContentTypeSlicerCache, "/"+opts.slicerCacheXML)
|
||||||
|
}
|
||||||
|
|
249
slicer_test.go
249
slicer_test.go
|
@ -5,12 +5,13 @@ import (
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAddSlicer(t *testing.T) {
|
func TestSlicer(t *testing.T) {
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
disable, colName := false, "_!@#$%^&*()-+=|\\/<>"
|
disable, colName := false, "_!@#$%^&*()-+=|\\/<>"
|
||||||
assert.NoError(t, f.SetCellValue("Sheet1", "B1", colName))
|
assert.NoError(t, f.SetCellValue("Sheet1", "B1", colName))
|
||||||
|
@ -45,8 +46,29 @@ func TestAddSlicer(t *testing.T) {
|
||||||
DisplayHeader: &disable,
|
DisplayHeader: &disable,
|
||||||
ItemDesc: true,
|
ItemDesc: true,
|
||||||
}))
|
}))
|
||||||
|
// Test get table slicers
|
||||||
|
slicers, err := f.GetSlicers("Sheet1")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "Column1", slicers[0].Name)
|
||||||
|
assert.Equal(t, "E1", slicers[0].Cell)
|
||||||
|
assert.Equal(t, "Sheet1", slicers[0].TableSheet)
|
||||||
|
assert.Equal(t, "Table1", slicers[0].TableName)
|
||||||
|
assert.Equal(t, "Column1", slicers[0].Caption)
|
||||||
|
assert.Equal(t, "Column1 1", slicers[1].Name)
|
||||||
|
assert.Equal(t, "I1", slicers[1].Cell)
|
||||||
|
assert.Equal(t, "Sheet1", slicers[1].TableSheet)
|
||||||
|
assert.Equal(t, "Table1", slicers[1].TableName)
|
||||||
|
assert.Equal(t, "Column1", slicers[1].Caption)
|
||||||
|
assert.Equal(t, colName, slicers[2].Name)
|
||||||
|
assert.Equal(t, "M1", slicers[2].Cell)
|
||||||
|
assert.Equal(t, "Sheet1", slicers[2].TableSheet)
|
||||||
|
assert.Equal(t, "Table1", slicers[2].TableName)
|
||||||
|
assert.Equal(t, colName, slicers[2].Caption)
|
||||||
|
assert.Equal(t, "Button1_Click", slicers[2].Macro)
|
||||||
|
assert.False(t, *slicers[2].DisplayHeader)
|
||||||
|
assert.True(t, slicers[2].ItemDesc)
|
||||||
// Test create two pivot tables in a new worksheet
|
// Test create two pivot tables in a new worksheet
|
||||||
_, err := f.NewSheet("Sheet2")
|
_, err = f.NewSheet("Sheet2")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
// Create some data in a sheet
|
// Create some data in a sheet
|
||||||
month := []string{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}
|
month := []string{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}
|
||||||
|
@ -116,6 +138,25 @@ func TestAddSlicer(t *testing.T) {
|
||||||
Caption: "Region",
|
Caption: "Region",
|
||||||
ItemDesc: true,
|
ItemDesc: true,
|
||||||
}))
|
}))
|
||||||
|
// Test get pivot table slicers
|
||||||
|
slicers, err = f.GetSlicers("Sheet2")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "Month", slicers[0].Name)
|
||||||
|
assert.Equal(t, "G42", slicers[0].Cell)
|
||||||
|
assert.Equal(t, "Sheet2", slicers[0].TableSheet)
|
||||||
|
assert.Equal(t, "PivotTable1", slicers[0].TableName)
|
||||||
|
assert.Equal(t, "Month", slicers[0].Caption)
|
||||||
|
assert.Equal(t, "Month 1", slicers[1].Name)
|
||||||
|
assert.Equal(t, "K42", slicers[1].Cell)
|
||||||
|
assert.Equal(t, "Sheet2", slicers[1].TableSheet)
|
||||||
|
assert.Equal(t, "PivotTable1", slicers[1].TableName)
|
||||||
|
assert.Equal(t, "Month", slicers[1].Caption)
|
||||||
|
assert.Equal(t, "Region", slicers[2].Name)
|
||||||
|
assert.Equal(t, "O42", slicers[2].Cell)
|
||||||
|
assert.Equal(t, "Sheet2", slicers[2].TableSheet)
|
||||||
|
assert.Equal(t, "PivotTable2", slicers[2].TableName)
|
||||||
|
assert.Equal(t, "Region", slicers[2].Caption)
|
||||||
|
assert.True(t, slicers[2].ItemDesc)
|
||||||
// Test add a table slicer with empty slicer options
|
// Test add a table slicer with empty slicer options
|
||||||
assert.Equal(t, ErrParameterRequired, f.AddSlicer("Sheet1", nil))
|
assert.Equal(t, ErrParameterRequired, f.AddSlicer("Sheet1", nil))
|
||||||
// Test add a table slicer with invalid slicer options
|
// Test add a table slicer with invalid slicer options
|
||||||
|
@ -167,6 +208,48 @@ func TestAddSlicer(t *testing.T) {
|
||||||
}), "XML syntax error on line 1: invalid UTF-8")
|
}), "XML syntax error on line 1: invalid UTF-8")
|
||||||
assert.NoError(t, f.Close())
|
assert.NoError(t, f.Close())
|
||||||
|
|
||||||
|
// Test open a workbook and get already exist slicers
|
||||||
|
f, err = OpenFile(workbookPath)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
slicers, err = f.GetSlicers("Sheet1")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "Column1", slicers[0].Name)
|
||||||
|
assert.Equal(t, "E1", slicers[0].Cell)
|
||||||
|
assert.Equal(t, "Sheet1", slicers[0].TableSheet)
|
||||||
|
assert.Equal(t, "Table1", slicers[0].TableName)
|
||||||
|
assert.Equal(t, "Column1", slicers[0].Caption)
|
||||||
|
assert.Equal(t, "Column1 1", slicers[1].Name)
|
||||||
|
assert.Equal(t, "I1", slicers[1].Cell)
|
||||||
|
assert.Equal(t, "Sheet1", slicers[1].TableSheet)
|
||||||
|
assert.Equal(t, "Table1", slicers[1].TableName)
|
||||||
|
assert.Equal(t, "Column1", slicers[1].Caption)
|
||||||
|
assert.Equal(t, colName, slicers[2].Name)
|
||||||
|
assert.Equal(t, "M1", slicers[2].Cell)
|
||||||
|
assert.Equal(t, "Sheet1", slicers[2].TableSheet)
|
||||||
|
assert.Equal(t, "Table1", slicers[2].TableName)
|
||||||
|
assert.Equal(t, colName, slicers[2].Caption)
|
||||||
|
assert.Equal(t, "Button1_Click", slicers[2].Macro)
|
||||||
|
assert.False(t, *slicers[2].DisplayHeader)
|
||||||
|
assert.True(t, slicers[2].ItemDesc)
|
||||||
|
slicers, err = f.GetSlicers("Sheet2")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "Month", slicers[0].Name)
|
||||||
|
assert.Equal(t, "G42", slicers[0].Cell)
|
||||||
|
assert.Equal(t, "Sheet2", slicers[0].TableSheet)
|
||||||
|
assert.Equal(t, "PivotTable1", slicers[0].TableName)
|
||||||
|
assert.Equal(t, "Month", slicers[0].Caption)
|
||||||
|
assert.Equal(t, "Month 1", slicers[1].Name)
|
||||||
|
assert.Equal(t, "K42", slicers[1].Cell)
|
||||||
|
assert.Equal(t, "Sheet2", slicers[1].TableSheet)
|
||||||
|
assert.Equal(t, "PivotTable1", slicers[1].TableName)
|
||||||
|
assert.Equal(t, "Month", slicers[1].Caption)
|
||||||
|
assert.Equal(t, "Region", slicers[2].Name)
|
||||||
|
assert.Equal(t, "O42", slicers[2].Cell)
|
||||||
|
assert.Equal(t, "Sheet2", slicers[2].TableSheet)
|
||||||
|
assert.Equal(t, "PivotTable2", slicers[2].TableName)
|
||||||
|
assert.Equal(t, "Region", slicers[2].Caption)
|
||||||
|
assert.True(t, slicers[2].ItemDesc)
|
||||||
|
|
||||||
// Test add a pivot table slicer with workbook which contains timeline
|
// Test add a pivot table slicer with workbook which contains timeline
|
||||||
f, err = OpenFile(workbookPath)
|
f, err = OpenFile(workbookPath)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
@ -274,6 +357,113 @@ func TestAddSlicer(t *testing.T) {
|
||||||
Caption: "Column1",
|
Caption: "Column1",
|
||||||
}), "XML syntax error on line 1: invalid UTF-8")
|
}), "XML syntax error on line 1: invalid UTF-8")
|
||||||
assert.NoError(t, f.Close())
|
assert.NoError(t, f.Close())
|
||||||
|
|
||||||
|
f = NewFile()
|
||||||
|
// Test get sheet slicers without slicer
|
||||||
|
slicers, err = f.GetSlicers("Sheet1")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Empty(t, slicers)
|
||||||
|
// Test get sheet slicers with not exist worksheet name
|
||||||
|
_, err = f.GetSlicers("SheetN")
|
||||||
|
assert.EqualError(t, err, "sheet SheetN does not exist")
|
||||||
|
assert.NoError(t, f.Close())
|
||||||
|
|
||||||
|
f, err = OpenFile(workbookPath)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
// Test get sheet slicers with unsupported charset slicer cache
|
||||||
|
f.Pkg.Store("xl/slicerCaches/slicerCache1.xml", MacintoshCyrillicCharset)
|
||||||
|
_, err = f.GetSlicers("Sheet1")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
// Test get sheet slicers with unsupported charset slicer
|
||||||
|
f.Pkg.Store("xl/slicers/slicer1.xml", MacintoshCyrillicCharset)
|
||||||
|
_, err = f.GetSlicers("Sheet1")
|
||||||
|
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
|
||||||
|
// Test get sheet slicers with invalid worksheet extension list
|
||||||
|
ws, ok = f.Sheet.Load("xl/worksheets/sheet1.xml")
|
||||||
|
assert.True(t, ok)
|
||||||
|
ws.(*xlsxWorksheet).ExtLst.Ext = "<>"
|
||||||
|
_, err = f.GetSlicers("Sheet1")
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.NoError(t, f.Close())
|
||||||
|
|
||||||
|
f, err = OpenFile(workbookPath)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
// Test get sheet slicers without slicer cache
|
||||||
|
f.Pkg.Range(func(k, v interface{}) bool {
|
||||||
|
if strings.Contains(k.(string), "xl/slicerCaches/slicerCache") {
|
||||||
|
f.Pkg.Delete(k.(string))
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
slicers, err = f.GetSlicers("Sheet1")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Empty(t, slicers)
|
||||||
|
assert.NoError(t, f.Close())
|
||||||
|
// Test open a workbook and get sheet slicer with invalid cell reference in the drawing part
|
||||||
|
f, err = OpenFile(workbookPath)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
f.Pkg.Store("xl/drawings/drawing1.xml", []byte(fmt.Sprintf(`<wsDr xmlns="%s"><twoCellAnchor><from><col>-1</col><row>-1</row></from><mc:AlternateContent><mc:Choice xmlns:sle15="%s"><graphicFrame><nvGraphicFramePr><cNvPr id="2" name="Column1"/></nvGraphicFramePr></graphicFrame></mc:Choice></mc:AlternateContent></twoCellAnchor></wsDr>`, NameSpaceDrawingMLSpreadSheet.Value, NameSpaceDrawingMLSlicerX15.Value)))
|
||||||
|
_, err = f.GetSlicers("Sheet1")
|
||||||
|
assert.Equal(t, newCoordinatesToCellNameError(0, 0), err)
|
||||||
|
// Test get sheet slicer without slicer shape in the drawing part
|
||||||
|
f.Drawings.Delete("xl/drawings/drawing1.xml")
|
||||||
|
f.Pkg.Store("xl/drawings/drawing1.xml", []byte(fmt.Sprintf(`<wsDr xmlns="%s"><twoCellAnchor/></wsDr>`, NameSpaceDrawingMLSpreadSheet.Value)))
|
||||||
|
_, err = f.GetSlicers("Sheet1")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
f.Drawings.Delete("xl/drawings/drawing1.xml")
|
||||||
|
// Test get sheet slicers with unsupported charset drawing part
|
||||||
|
f.Pkg.Store("xl/drawings/drawing1.xml", MacintoshCyrillicCharset)
|
||||||
|
_, err = f.GetSlicers("Sheet1")
|
||||||
|
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
|
||||||
|
// Test get sheet slicers with unsupported charset table
|
||||||
|
f.Pkg.Store("xl/tables/table1.xml", MacintoshCyrillicCharset)
|
||||||
|
_, err = f.GetSlicers("Sheet1")
|
||||||
|
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
|
||||||
|
// Test get sheet slicers with unsupported charset pivot table
|
||||||
|
f.Pkg.Store("xl/pivotTables/pivotTable1.xml", MacintoshCyrillicCharset)
|
||||||
|
_, err = f.GetSlicers("Sheet2")
|
||||||
|
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
|
||||||
|
assert.NoError(t, f.Close())
|
||||||
|
|
||||||
|
// Test create a workbook and get sheet slicer with invalid cell reference in the drawing part
|
||||||
|
f = NewFile()
|
||||||
|
assert.NoError(t, f.AddTable("Sheet1", &Table{
|
||||||
|
Name: "Table1",
|
||||||
|
Range: "A1:D5",
|
||||||
|
}))
|
||||||
|
assert.NoError(t, f.AddSlicer("Sheet1", &SlicerOptions{
|
||||||
|
Name: "Column1",
|
||||||
|
Cell: "E1",
|
||||||
|
TableSheet: "Sheet1",
|
||||||
|
TableName: "Table1",
|
||||||
|
Caption: "Column1",
|
||||||
|
}))
|
||||||
|
drawing, ok := f.Drawings.Load("xl/drawings/drawing1.xml")
|
||||||
|
assert.True(t, ok)
|
||||||
|
drawing.(*xlsxWsDr).TwoCellAnchor[0].From = &xlsxFrom{Col: -1, Row: -1}
|
||||||
|
_, err = f.GetSlicers("Sheet1")
|
||||||
|
assert.Equal(t, newCoordinatesToCellNameError(0, 0), err)
|
||||||
|
assert.NoError(t, f.Close())
|
||||||
|
|
||||||
|
// Test open a workbook and delete slicers
|
||||||
|
f, err = OpenFile(workbookPath)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
for _, name := range []string{colName, "Column1 1", "Column1"} {
|
||||||
|
assert.NoError(t, f.DeleteSlicer(name))
|
||||||
|
}
|
||||||
|
for _, name := range []string{"Month", "Month 1", "Region"} {
|
||||||
|
assert.NoError(t, f.DeleteSlicer(name))
|
||||||
|
}
|
||||||
|
// Test delete slicer with no exits slicer name
|
||||||
|
assert.Equal(t, newNoExistSlicerError("x"), f.DeleteSlicer("x"))
|
||||||
|
assert.NoError(t, f.Close())
|
||||||
|
|
||||||
|
// Test open a workbook and delete sheet slicer with unsupported charset slicer cache
|
||||||
|
f, err = OpenFile(workbookPath)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
f.Pkg.Store("xl/slicers/slicer1.xml", MacintoshCyrillicCharset)
|
||||||
|
assert.EqualError(t, f.DeleteSlicer("Column1"), "XML syntax error on line 1: invalid UTF-8")
|
||||||
|
assert.NoError(t, f.Close())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAddSheetSlicer(t *testing.T) {
|
func TestAddSheetSlicer(t *testing.T) {
|
||||||
|
@ -296,36 +486,81 @@ func TestAddSheetTableSlicer(t *testing.T) {
|
||||||
func TestSetSlicerCache(t *testing.T) {
|
func TestSetSlicerCache(t *testing.T) {
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
f.Pkg.Store("xl/slicerCaches/slicerCache1.xml", MacintoshCyrillicCharset)
|
f.Pkg.Store("xl/slicerCaches/slicerCache1.xml", MacintoshCyrillicCharset)
|
||||||
_, err := f.setSlicerCache("Sheet1", 1, &SlicerOptions{}, &Table{}, nil)
|
_, err := f.setSlicerCache(1, &SlicerOptions{}, &Table{}, nil)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NoError(t, f.Close())
|
assert.NoError(t, f.Close())
|
||||||
|
|
||||||
f = NewFile()
|
f = NewFile()
|
||||||
|
|
||||||
f.Pkg.Store("xl/slicerCaches/slicerCache2.xml", []byte(fmt.Sprintf(`<slicerCacheDefinition xmlns="%s" name="Slicer2" sourceName="B1"><extLst><ext uri="%s"/></extLst></slicerCacheDefinition>`, NameSpaceSpreadSheetX14.Value, ExtURISlicerCacheDefinition)))
|
f.Pkg.Store("xl/slicerCaches/slicerCache2.xml", []byte(fmt.Sprintf(`<slicerCacheDefinition xmlns="%s" name="Slicer2" sourceName="B1"><extLst><ext uri="%s"/></extLst></slicerCacheDefinition>`, NameSpaceSpreadSheetX14.Value, ExtURISlicerCacheDefinition)))
|
||||||
_, err = f.setSlicerCache("Sheet1", 1, &SlicerOptions{}, &Table{}, nil)
|
_, err = f.setSlicerCache(1, &SlicerOptions{}, &Table{}, nil)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NoError(t, f.Close())
|
assert.NoError(t, f.Close())
|
||||||
|
|
||||||
f = NewFile()
|
f = NewFile()
|
||||||
f.Pkg.Store("xl/slicerCaches/slicerCache2.xml", []byte(fmt.Sprintf(`<slicerCacheDefinition xmlns="%s" name="Slicer1" sourceName="B1"><extLst><ext uri="%s"/></extLst></slicerCacheDefinition>`, NameSpaceSpreadSheetX14.Value, ExtURISlicerCacheDefinition)))
|
f.Pkg.Store("xl/slicerCaches/slicerCache2.xml", []byte(fmt.Sprintf(`<slicerCacheDefinition xmlns="%s" name="Slicer1" sourceName="B1"><extLst><ext uri="%s"/></extLst></slicerCacheDefinition>`, NameSpaceSpreadSheetX14.Value, ExtURISlicerCacheDefinition)))
|
||||||
_, err = f.setSlicerCache("Sheet1", 1, &SlicerOptions{}, &Table{}, nil)
|
_, err = f.setSlicerCache(1, &SlicerOptions{}, &Table{}, nil)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NoError(t, f.Close())
|
assert.NoError(t, f.Close())
|
||||||
|
|
||||||
f = NewFile()
|
f = NewFile()
|
||||||
f.Pkg.Store("xl/slicerCaches/slicerCache2.xml", []byte(fmt.Sprintf(`<slicerCacheDefinition xmlns="%s" name="Slicer1" sourceName="B1"><extLst><ext uri="%s"><tableSlicerCache tableId="1" column="2"/></ext></extLst></slicerCacheDefinition>`, NameSpaceSpreadSheetX14.Value, ExtURISlicerCacheDefinition)))
|
f.Pkg.Store("xl/slicerCaches/slicerCache2.xml", []byte(fmt.Sprintf(`<slicerCacheDefinition xmlns="%s" name="Slicer1" sourceName="B1"><extLst><ext uri="%s"><tableSlicerCache tableId="1" column="2"/></ext></extLst></slicerCacheDefinition>`, NameSpaceSpreadSheetX14.Value, ExtURISlicerCacheDefinition)))
|
||||||
_, err = f.setSlicerCache("Sheet1", 1, &SlicerOptions{}, &Table{tID: 1}, nil)
|
_, err = f.setSlicerCache(1, &SlicerOptions{}, &Table{tID: 1}, nil)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NoError(t, f.Close())
|
assert.NoError(t, f.Close())
|
||||||
|
|
||||||
f = NewFile()
|
f = NewFile()
|
||||||
f.Pkg.Store("xl/slicerCaches/slicerCache2.xml", []byte(fmt.Sprintf(`<slicerCacheDefinition xmlns="%s" name="Slicer1" sourceName="B1"></slicerCacheDefinition>`, NameSpaceSpreadSheetX14.Value)))
|
f.Pkg.Store("xl/slicerCaches/slicerCache2.xml", []byte(fmt.Sprintf(`<slicerCacheDefinition xmlns="%s" name="Slicer1" sourceName="B1"></slicerCacheDefinition>`, NameSpaceSpreadSheetX14.Value)))
|
||||||
_, err = f.setSlicerCache("Sheet1", 1, &SlicerOptions{}, &Table{tID: 1}, nil)
|
_, err = f.setSlicerCache(1, &SlicerOptions{}, &Table{tID: 1}, nil)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NoError(t, f.Close())
|
assert.NoError(t, f.Close())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDeleteSlicer(t *testing.T) {
|
||||||
|
f, slicerXML := NewFile(), "xl/slicers/slicer1.xml"
|
||||||
|
assert.NoError(t, f.AddTable("Sheet1", &Table{
|
||||||
|
Name: "Table1",
|
||||||
|
Range: "A1:D5",
|
||||||
|
}))
|
||||||
|
assert.NoError(t, f.AddSlicer("Sheet1", &SlicerOptions{
|
||||||
|
Name: "Column1",
|
||||||
|
Cell: "E1",
|
||||||
|
TableSheet: "Sheet1",
|
||||||
|
TableName: "Table1",
|
||||||
|
Caption: "Column1",
|
||||||
|
}))
|
||||||
|
// Test delete sheet slicers with invalid worksheet extension list
|
||||||
|
ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml")
|
||||||
|
assert.True(t, ok)
|
||||||
|
ws.(*xlsxWorksheet).ExtLst.Ext = "<>"
|
||||||
|
assert.Error(t, f.deleteSlicer(SlicerOptions{
|
||||||
|
slicerXML: slicerXML,
|
||||||
|
slicerSheetName: "Sheet1",
|
||||||
|
Name: "Column1",
|
||||||
|
}))
|
||||||
|
// Test delete slicer with unsupported charset worksheet
|
||||||
|
f.Sheet.Delete("xl/worksheets/sheet1.xml")
|
||||||
|
f.Pkg.Store("xl/worksheets/sheet1.xml", MacintoshCyrillicCharset)
|
||||||
|
assert.EqualError(t, f.deleteSlicer(SlicerOptions{
|
||||||
|
slicerXML: slicerXML,
|
||||||
|
slicerSheetName: "Sheet1",
|
||||||
|
Name: "Column1",
|
||||||
|
}), "XML syntax error on line 1: invalid UTF-8")
|
||||||
|
// Test delete slicer with unsupported charset slicer
|
||||||
|
f.Pkg.Store(slicerXML, MacintoshCyrillicCharset)
|
||||||
|
assert.EqualError(t, f.deleteSlicer(SlicerOptions{slicerXML: slicerXML}), "XML syntax error on line 1: invalid UTF-8")
|
||||||
|
assert.NoError(t, f.Close())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeleteSlicerCache(t *testing.T) {
|
||||||
|
f := NewFile()
|
||||||
|
// Test delete slicer cache with unsupported charset workbook
|
||||||
|
f.WorkBook = nil
|
||||||
|
f.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
|
||||||
|
assert.EqualError(t, f.deleteSlicerCache(nil, SlicerOptions{}), "XML syntax error on line 1: invalid UTF-8")
|
||||||
|
assert.NoError(t, f.Close())
|
||||||
|
}
|
||||||
|
|
||||||
func TestAddSlicerCache(t *testing.T) {
|
func TestAddSlicerCache(t *testing.T) {
|
||||||
f := NewFile()
|
f := NewFile()
|
||||||
f.ContentTypes = nil
|
f.ContentTypes = nil
|
||||||
|
|
24
table.go
24
table.go
|
@ -173,11 +173,11 @@ func (f *File) DeleteTable(name string) error {
|
||||||
if err := checkDefinedName(name); err != nil {
|
if err := checkDefinedName(name); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, sheet := range f.GetSheetList() {
|
tbls, err := f.getTables()
|
||||||
tables, err := f.GetTables(sheet)
|
if err != nil {
|
||||||
if err != nil {
|
return err
|
||||||
return err
|
}
|
||||||
}
|
for sheet, tables := range tbls {
|
||||||
for _, table := range tables {
|
for _, table := range tables {
|
||||||
if table.Name != name {
|
if table.Name != name {
|
||||||
continue
|
continue
|
||||||
|
@ -201,6 +201,20 @@ func (f *File) DeleteTable(name string) error {
|
||||||
return newNoExistTableError(name)
|
return newNoExistTableError(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getTables provides a function to get all tables in a workbook.
|
||||||
|
func (f *File) getTables() (map[string][]Table, error) {
|
||||||
|
tables := map[string][]Table{}
|
||||||
|
for _, sheetName := range f.GetSheetList() {
|
||||||
|
tbls, err := f.GetTables(sheetName)
|
||||||
|
e := ErrSheetNotExist{sheetName}
|
||||||
|
if err != nil && err.Error() != newNotWorksheetError(sheetName).Error() && err.Error() != e.Error() {
|
||||||
|
return tables, err
|
||||||
|
}
|
||||||
|
tables[sheetName] = append(tables[sheetName], tbls...)
|
||||||
|
}
|
||||||
|
return tables, nil
|
||||||
|
}
|
||||||
|
|
||||||
// countTables provides a function to get table files count storage in the
|
// countTables provides a function to get table files count storage in the
|
||||||
// folder xl/tables.
|
// folder xl/tables.
|
||||||
func (f *File) countTables() int {
|
func (f *File) countTables() int {
|
||||||
|
|
|
@ -24,7 +24,7 @@ type decodeCellAnchor struct {
|
||||||
Sp *decodeSp `xml:"sp"`
|
Sp *decodeSp `xml:"sp"`
|
||||||
Pic *decodePic `xml:"pic"`
|
Pic *decodePic `xml:"pic"`
|
||||||
ClientData *decodeClientData `xml:"clientData"`
|
ClientData *decodeClientData `xml:"clientData"`
|
||||||
AlternateContent []*xlsxAlternateContent `xml:"mc:AlternateContent"`
|
AlternateContent []*xlsxAlternateContent `xml:"AlternateContent"`
|
||||||
Content string `xml:",innerxml"`
|
Content string `xml:",innerxml"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,6 +46,28 @@ type decodeCellAnchorPos struct {
|
||||||
ClientData *xlsxInnerXML `xml:"clientData"`
|
ClientData *xlsxInnerXML `xml:"clientData"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// decodeChoice defines the structure used to deserialize the mc:Choice element.
|
||||||
|
type decodeChoice struct {
|
||||||
|
XMLName xml.Name `xml:"Choice"`
|
||||||
|
XMLNSA14 string `xml:"a14,attr"`
|
||||||
|
XMLNSSle15 string `xml:"sle15,attr"`
|
||||||
|
Requires string `xml:"Requires,attr"`
|
||||||
|
GraphicFrame decodeGraphicFrame `xml:"graphicFrame"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodeGraphicFrame defines the structure used to deserialize the
|
||||||
|
// xdr:graphicFrame element.
|
||||||
|
type decodeGraphicFrame struct {
|
||||||
|
Macro string `xml:"macro,attr"`
|
||||||
|
NvGraphicFramePr decodeNvGraphicFramePr `xml:"nvGraphicFramePr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodeNvGraphicFramePr defines the structure used to deserialize the
|
||||||
|
// xdr:nvGraphicFramePr element.
|
||||||
|
type decodeNvGraphicFramePr struct {
|
||||||
|
CNvPr decodeCNvPr `xml:"cNvPr"`
|
||||||
|
}
|
||||||
|
|
||||||
// decodeSp defines the structure used to deserialize the sp element.
|
// decodeSp defines the structure used to deserialize the sp element.
|
||||||
type decodeSp struct {
|
type decodeSp struct {
|
||||||
Macro string `xml:"macro,attr,omitempty"`
|
Macro string `xml:"macro,attr,omitempty"`
|
||||||
|
@ -56,7 +78,7 @@ type decodeSp struct {
|
||||||
SpPr *decodeSpPr `xml:"spPr"`
|
SpPr *decodeSpPr `xml:"spPr"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// decodeSp (Non-Visual Properties for a Shape) directly maps the nvSpPr
|
// decodeNvSpPr (Non-Visual Properties for a Shape) directly maps the nvSpPr
|
||||||
// element. This element specifies all non-visual properties for a shape. This
|
// element. This element specifies all non-visual properties for a shape. This
|
||||||
// element is a container for the non-visual identification properties, shape
|
// element is a container for the non-visual identification properties, shape
|
||||||
// properties and application properties that are to be associated with a
|
// properties and application properties that are to be associated with a
|
||||||
|
|
|
@ -149,9 +149,10 @@ type xlsxX15SlicerCaches struct {
|
||||||
// decodeTableSlicerCache defines the structure used to parse the
|
// decodeTableSlicerCache defines the structure used to parse the
|
||||||
// x15:tableSlicerCache element of the table slicer cache.
|
// x15:tableSlicerCache element of the table slicer cache.
|
||||||
type decodeTableSlicerCache struct {
|
type decodeTableSlicerCache struct {
|
||||||
XMLName xml.Name `xml:"tableSlicerCache"`
|
XMLName xml.Name `xml:"tableSlicerCache"`
|
||||||
TableID int `xml:"tableId,attr"`
|
TableID int `xml:"tableId,attr"`
|
||||||
Column int `xml:"column,attr"`
|
Column int `xml:"column,attr"`
|
||||||
|
SortOrder string `xml:"sortOrder,attr"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// decodeSlicerList defines the structure used to parse the x14:slicerList
|
// decodeSlicerList defines the structure used to parse the x14:slicerList
|
||||||
|
|
Loading…
Reference in New Issue