Introduce 2 new functions SetCalcProps and GetCalcProps (#2098)
- Add new CalcPropsOptions data type - Support assign exported data structure fields value to internal data structure fields dynamically by specified fields name list - Simplify code for function SetAppProps, SetDocProps, SetWorkbookProps, GetWorkbookProps and getPivotTable - Update unit tests
This commit is contained in:
parent
c6d161fc76
commit
aef20e226c
57
docProps.go
57
docProps.go
|
@ -65,34 +65,15 @@ import (
|
|||
// AppVersion: "16.0000",
|
||||
// })
|
||||
func (f *File) SetAppProps(appProperties *AppProperties) error {
|
||||
var (
|
||||
app *xlsxProperties
|
||||
err error
|
||||
field string
|
||||
fields []string
|
||||
immutable, mutable reflect.Value
|
||||
output []byte
|
||||
)
|
||||
app = new(xlsxProperties)
|
||||
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLPathDocPropsApp)))).
|
||||
app := new(xlsxProperties)
|
||||
if err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLPathDocPropsApp)))).
|
||||
Decode(app); err != nil && err != io.EOF {
|
||||
return err
|
||||
}
|
||||
fields = []string{"Application", "ScaleCrop", "DocSecurity", "Company", "LinksUpToDate", "HyperlinksChanged", "AppVersion"}
|
||||
immutable, mutable = reflect.ValueOf(*appProperties), reflect.ValueOf(app).Elem()
|
||||
for _, field = range fields {
|
||||
immutableField := immutable.FieldByName(field)
|
||||
switch immutableField.Kind() {
|
||||
case reflect.Bool:
|
||||
mutable.FieldByName(field).SetBool(immutableField.Bool())
|
||||
case reflect.Int:
|
||||
mutable.FieldByName(field).SetInt(immutableField.Int())
|
||||
default:
|
||||
mutable.FieldByName(field).SetString(immutableField.String())
|
||||
}
|
||||
}
|
||||
setNoPtrFieldsVal([]string{"Application", "ScaleCrop", "DocSecurity", "Company", "LinksUpToDate", "HyperlinksChanged", "AppVersion"},
|
||||
reflect.ValueOf(*appProperties), reflect.ValueOf(app).Elem())
|
||||
app.Vt = NameSpaceDocumentPropertiesVariantTypes.Value
|
||||
output, err = xml.Marshal(app)
|
||||
output, err := xml.Marshal(app)
|
||||
f.saveFileList(defaultXMLPathDocPropsApp, output)
|
||||
return err
|
||||
}
|
||||
|
@ -180,22 +161,12 @@ func (f *File) GetAppProps() (ret *AppProperties, err error) {
|
|||
// Version: "1.0.0",
|
||||
// })
|
||||
func (f *File) SetDocProps(docProperties *DocProperties) error {
|
||||
var (
|
||||
core *decodeCoreProperties
|
||||
err error
|
||||
field, val string
|
||||
fields []string
|
||||
immutable, mutable reflect.Value
|
||||
newProps *xlsxCoreProperties
|
||||
output []byte
|
||||
)
|
||||
|
||||
core = new(decodeCoreProperties)
|
||||
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLPathDocPropsCore)))).
|
||||
core := new(decodeCoreProperties)
|
||||
if err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLPathDocPropsCore)))).
|
||||
Decode(core); err != nil && err != io.EOF {
|
||||
return err
|
||||
}
|
||||
newProps = &xlsxCoreProperties{
|
||||
newProps := &xlsxCoreProperties{
|
||||
Dc: NameSpaceDublinCore,
|
||||
Dcterms: NameSpaceDublinCoreTerms,
|
||||
Dcmitype: NameSpaceDublinCoreMetadataInitiative,
|
||||
|
@ -219,23 +190,17 @@ func (f *File) SetDocProps(docProperties *DocProperties) error {
|
|||
if core.Modified != nil {
|
||||
newProps.Modified = &xlsxDcTerms{Type: core.Modified.Type, Text: core.Modified.Text}
|
||||
}
|
||||
fields = []string{
|
||||
setNoPtrFieldsVal([]string{
|
||||
"Category", "ContentStatus", "Creator", "Description", "Identifier", "Keywords",
|
||||
"LastModifiedBy", "Revision", "Subject", "Title", "Language", "Version",
|
||||
}
|
||||
immutable, mutable = reflect.ValueOf(*docProperties), reflect.ValueOf(newProps).Elem()
|
||||
for _, field = range fields {
|
||||
if val = immutable.FieldByName(field).String(); val != "" {
|
||||
mutable.FieldByName(field).SetString(val)
|
||||
}
|
||||
}
|
||||
}, reflect.ValueOf(*docProperties), reflect.ValueOf(newProps).Elem())
|
||||
if docProperties.Created != "" {
|
||||
newProps.Created = &xlsxDcTerms{Type: "dcterms:W3CDTF", Text: docProperties.Created}
|
||||
}
|
||||
if docProperties.Modified != "" {
|
||||
newProps.Modified = &xlsxDcTerms{Type: "dcterms:W3CDTF", Text: docProperties.Modified}
|
||||
}
|
||||
output, err = xml.Marshal(newProps)
|
||||
output, err := xml.Marshal(newProps)
|
||||
f.saveFileList(defaultXMLPathDocPropsCore, output)
|
||||
|
||||
return err
|
||||
|
|
49
lib.go
49
lib.go
|
@ -21,6 +21,7 @@ import (
|
|||
"math"
|
||||
"math/big"
|
||||
"os"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -878,6 +879,54 @@ func continuedFraction(n float64, i int64, limit int64, prec float64) *big.Rat {
|
|||
return res
|
||||
}
|
||||
|
||||
// assignFieldValue assigns the value from an immutable reflect.Value to a
|
||||
// mutable reflect.Value based on the type of the immutable value.
|
||||
func assignFieldValue(field string, immutable, mutable reflect.Value) {
|
||||
switch immutable.Kind() {
|
||||
case reflect.Bool:
|
||||
mutable.FieldByName(field).SetBool(immutable.Bool())
|
||||
case reflect.Int:
|
||||
mutable.FieldByName(field).SetInt(immutable.Int())
|
||||
default:
|
||||
mutable.FieldByName(field).SetString(immutable.String())
|
||||
}
|
||||
}
|
||||
|
||||
// setNoPtrFieldsVal assigns values from the pointer or no-pointer structs
|
||||
// fields (immutable) value to no-pointer struct field.
|
||||
func setNoPtrFieldsVal(fields []string, immutable, mutable reflect.Value) {
|
||||
for _, field := range fields {
|
||||
immutableField := immutable.FieldByName(field)
|
||||
if immutableField.Kind() == reflect.Ptr {
|
||||
if immutableField.IsValid() && !immutableField.IsNil() {
|
||||
assignFieldValue(field, immutableField.Elem(), mutable)
|
||||
}
|
||||
continue
|
||||
}
|
||||
assignFieldValue(field, immutableField, mutable)
|
||||
}
|
||||
}
|
||||
|
||||
// setPtrFieldsVal assigns values from the pointer or no-pointer structs
|
||||
// fields (immutable) value to pointer struct field.
|
||||
func setPtrFieldsVal(fields []string, immutable, mutable reflect.Value) {
|
||||
for _, field := range fields {
|
||||
immutableField := immutable.FieldByName(field)
|
||||
if immutableField.Kind() == reflect.Ptr {
|
||||
if immutableField.IsValid() && !immutableField.IsNil() {
|
||||
mutable.FieldByName(field).Set(immutableField.Elem())
|
||||
}
|
||||
continue
|
||||
}
|
||||
if immutableField.IsZero() {
|
||||
continue
|
||||
}
|
||||
ptr := reflect.New(immutableField.Type())
|
||||
ptr.Elem().Set(immutableField)
|
||||
mutable.FieldByName(field).Set(ptr)
|
||||
}
|
||||
}
|
||||
|
||||
// Stack defined an abstract data type that serves as a collection of elements.
|
||||
type Stack struct {
|
||||
list *list.List
|
||||
|
|
|
@ -883,14 +883,10 @@ func (f *File) getPivotTable(sheet, pivotTableXML, pivotCacheRels string) (Pivot
|
|||
opts.DataRange = pc.CacheSource.WorksheetSource.Name
|
||||
_ = f.getPivotTableDataRange(&opts)
|
||||
}
|
||||
fields := []string{"RowGrandTotals", "ColGrandTotals", "ShowDrill", "UseAutoFormatting", "PageOverThenDown", "MergeItem", "CompactData", "ShowError"}
|
||||
immutable, mutable := reflect.ValueOf(*pt), reflect.ValueOf(&opts).Elem()
|
||||
for _, field := range fields {
|
||||
immutableField := immutable.FieldByName(field)
|
||||
if immutableField.Kind() == reflect.Ptr && !immutableField.IsNil() && immutableField.Elem().Kind() == reflect.Bool {
|
||||
mutable.FieldByName(field).SetBool(immutableField.Elem().Bool())
|
||||
}
|
||||
}
|
||||
setPtrFieldsVal([]string{
|
||||
"RowGrandTotals", "ColGrandTotals", "ShowDrill",
|
||||
"UseAutoFormatting", "PageOverThenDown", "MergeItem", "CompactData", "ShowError",
|
||||
}, reflect.ValueOf(*pt), reflect.ValueOf(&opts).Elem())
|
||||
if si := pt.PivotTableStyleInfo; si != nil {
|
||||
opts.ShowRowHeaders = si.ShowRowHeaders
|
||||
opts.ShowColHeaders = si.ShowColHeaders
|
||||
|
@ -982,17 +978,8 @@ func extractPivotTableField(data string, fld *xlsxPivotField) PivotTableField {
|
|||
ShowAll: fld.ShowAll,
|
||||
InsertBlankRow: fld.InsertBlankRow,
|
||||
}
|
||||
fields := []string{"Compact", "Name", "Outline", "Subtotal", "DefaultSubtotal"}
|
||||
immutable, mutable := reflect.ValueOf(*fld), reflect.ValueOf(&pivotTableField).Elem()
|
||||
for _, field := range fields {
|
||||
immutableField := immutable.FieldByName(field)
|
||||
if immutableField.Kind() == reflect.String {
|
||||
mutable.FieldByName(field).SetString(immutableField.String())
|
||||
}
|
||||
if immutableField.Kind() == reflect.Ptr && !immutableField.IsNil() && immutableField.Elem().Kind() == reflect.Bool {
|
||||
mutable.FieldByName(field).SetBool(immutableField.Elem().Bool())
|
||||
}
|
||||
}
|
||||
setPtrFieldsVal([]string{"Compact", "Name", "Outline", "DefaultSubtotal"},
|
||||
reflect.ValueOf(*fld), reflect.ValueOf(&pivotTableField).Elem())
|
||||
return pivotTableField
|
||||
}
|
||||
|
||||
|
|
82
workbook.go
82
workbook.go
|
@ -16,12 +16,16 @@ import (
|
|||
"encoding/xml"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SetWorkbookProps provides a function to sets workbook properties.
|
||||
func (f *File) SetWorkbookProps(opts *WorkbookPropsOptions) error {
|
||||
if opts == nil {
|
||||
return nil
|
||||
}
|
||||
wb, err := f.workbookReader()
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -29,19 +33,9 @@ func (f *File) SetWorkbookProps(opts *WorkbookPropsOptions) error {
|
|||
if wb.WorkbookPr == nil {
|
||||
wb.WorkbookPr = new(xlsxWorkbookPr)
|
||||
}
|
||||
if opts == nil {
|
||||
return nil
|
||||
}
|
||||
if opts.Date1904 != nil {
|
||||
wb.WorkbookPr.Date1904 = *opts.Date1904
|
||||
}
|
||||
if opts.FilterPrivacy != nil {
|
||||
wb.WorkbookPr.FilterPrivacy = *opts.FilterPrivacy
|
||||
}
|
||||
if opts.CodeName != nil {
|
||||
wb.WorkbookPr.CodeName = *opts.CodeName
|
||||
}
|
||||
return nil
|
||||
setNoPtrFieldsVal([]string{"Date1904", "FilterPrivacy", "CodeName"},
|
||||
reflect.ValueOf(*opts), reflect.ValueOf(wb.WorkbookPr).Elem())
|
||||
return err
|
||||
}
|
||||
|
||||
// GetWorkbookProps provides a function to gets workbook properties.
|
||||
|
@ -51,11 +45,63 @@ func (f *File) GetWorkbookProps() (WorkbookPropsOptions, error) {
|
|||
if err != nil {
|
||||
return opts, err
|
||||
}
|
||||
if wb.WorkbookPr != nil {
|
||||
opts.Date1904 = boolPtr(wb.WorkbookPr.Date1904)
|
||||
opts.FilterPrivacy = boolPtr(wb.WorkbookPr.FilterPrivacy)
|
||||
opts.CodeName = stringPtr(wb.WorkbookPr.CodeName)
|
||||
if wb.WorkbookPr == nil {
|
||||
return opts, err
|
||||
}
|
||||
setPtrFieldsVal([]string{"Date1904", "FilterPrivacy", "CodeName"},
|
||||
reflect.ValueOf(*wb.WorkbookPr), reflect.ValueOf(&opts).Elem())
|
||||
return opts, err
|
||||
}
|
||||
|
||||
// SetCalcProps provides a function to sets calculation properties.
|
||||
func (f *File) SetCalcProps(opts *CalcPropsOptions) error {
|
||||
if opts == nil {
|
||||
return nil
|
||||
}
|
||||
wb, err := f.workbookReader()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if wb.CalcPr == nil {
|
||||
wb.CalcPr = new(xlsxCalcPr)
|
||||
}
|
||||
setNoPtrFieldsVal([]string{
|
||||
"CalcCompleted", "CalcOnSave", "ForceFullCalc", "FullCalcOnLoad", "FullPrecision", "Iterate",
|
||||
"IterateDelta",
|
||||
"CalcMode", "RefMode",
|
||||
}, reflect.ValueOf(*opts), reflect.ValueOf(wb.CalcPr).Elem())
|
||||
if opts.CalcID != nil {
|
||||
wb.CalcPr.CalcID = int(*opts.CalcID)
|
||||
}
|
||||
if opts.ConcurrentManualCount != nil {
|
||||
wb.CalcPr.ConcurrentManualCount = int(*opts.ConcurrentManualCount)
|
||||
}
|
||||
if opts.IterateCount != nil {
|
||||
wb.CalcPr.IterateCount = int(*opts.IterateCount)
|
||||
}
|
||||
wb.CalcPr.ConcurrentCalc = opts.ConcurrentCalc
|
||||
return err
|
||||
}
|
||||
|
||||
// GetCalcProps provides a function to gets calculation properties.
|
||||
func (f *File) GetCalcProps() (CalcPropsOptions, error) {
|
||||
var opts CalcPropsOptions
|
||||
wb, err := f.workbookReader()
|
||||
if err != nil {
|
||||
return opts, err
|
||||
}
|
||||
if wb.CalcPr == nil {
|
||||
return opts, err
|
||||
}
|
||||
setPtrFieldsVal([]string{
|
||||
"CalcCompleted", "CalcOnSave", "ForceFullCalc", "FullCalcOnLoad", "FullPrecision", "Iterate",
|
||||
"IterateDelta",
|
||||
"CalcMode", "RefMode",
|
||||
}, reflect.ValueOf(*wb.CalcPr), reflect.ValueOf(&opts).Elem())
|
||||
opts.CalcID = uintPtr(uint(wb.CalcPr.CalcID))
|
||||
opts.ConcurrentManualCount = uintPtr(uint(wb.CalcPr.ConcurrentManualCount))
|
||||
opts.IterateCount = uintPtr(uint(wb.CalcPr.IterateCount))
|
||||
opts.ConcurrentCalc = wb.CalcPr.ConcurrentCalc
|
||||
return opts, err
|
||||
}
|
||||
|
||||
|
@ -99,7 +145,7 @@ func (f *File) ProtectWorkbook(opts *WorkbookProtectionOptions) error {
|
|||
wb.WorkbookProtection.WorkbookHashValue = hashValue
|
||||
wb.WorkbookProtection.WorkbookSpinCount = int(workbookProtectionSpinCount)
|
||||
}
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
// UnprotectWorkbook provides a function to remove protection for workbook,
|
||||
|
|
|
@ -21,6 +21,10 @@ func TestWorkbookProps(t *testing.T) {
|
|||
opts, err := f.GetWorkbookProps()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected, opts)
|
||||
wb.WorkbookPr = nil
|
||||
opts, err = f.GetWorkbookProps()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, WorkbookPropsOptions{}, opts)
|
||||
// Test set workbook properties with unsupported charset workbook
|
||||
f.WorkBook = nil
|
||||
f.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
|
||||
|
@ -32,6 +36,38 @@ func TestWorkbookProps(t *testing.T) {
|
|||
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
|
||||
}
|
||||
|
||||
func TestCalcProps(t *testing.T) {
|
||||
f := NewFile()
|
||||
assert.NoError(t, f.SetCalcProps(nil))
|
||||
wb, err := f.workbookReader()
|
||||
assert.NoError(t, err)
|
||||
wb.CalcPr = nil
|
||||
expected := CalcPropsOptions{
|
||||
FullCalcOnLoad: boolPtr(true),
|
||||
CalcID: uintPtr(122211),
|
||||
ConcurrentManualCount: uintPtr(5),
|
||||
IterateCount: uintPtr(10),
|
||||
ConcurrentCalc: boolPtr(true),
|
||||
}
|
||||
assert.NoError(t, f.SetCalcProps(&expected))
|
||||
opts, err := f.GetCalcProps()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected, opts)
|
||||
wb.CalcPr = nil
|
||||
opts, err = f.GetCalcProps()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, CalcPropsOptions{}, opts)
|
||||
// Test set workbook properties with unsupported charset workbook
|
||||
f.WorkBook = nil
|
||||
f.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
|
||||
assert.EqualError(t, f.SetCalcProps(&expected), "XML syntax error on line 1: invalid UTF-8")
|
||||
// Test get workbook properties with unsupported charset workbook
|
||||
f.WorkBook = nil
|
||||
f.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
|
||||
_, err = f.GetCalcProps()
|
||||
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
|
||||
}
|
||||
|
||||
func TestDeleteWorkbookRels(t *testing.T) {
|
||||
f := NewFile()
|
||||
// Test delete pivot table without worksheet relationships
|
||||
|
|
|
@ -315,7 +315,7 @@ type xlsxDefinedName struct {
|
|||
// displaying the results as values in the cells that contain the formulas.
|
||||
type xlsxCalcPr struct {
|
||||
CalcCompleted bool `xml:"calcCompleted,attr,omitempty"`
|
||||
CalcID string `xml:"calcId,attr,omitempty"`
|
||||
CalcID int `xml:"calcId,attr,omitempty"`
|
||||
CalcMode string `xml:"calcMode,attr,omitempty"`
|
||||
CalcOnSave bool `xml:"calcOnSave,attr,omitempty"`
|
||||
ConcurrentCalc *bool `xml:"concurrentCalc,attr"`
|
||||
|
@ -384,6 +384,24 @@ type DefinedName struct {
|
|||
Scope string
|
||||
}
|
||||
|
||||
// CalcPropsOptions defines the collection of properties the application uses to
|
||||
// record calculation status and details.
|
||||
type CalcPropsOptions struct {
|
||||
CalcID *uint `xml:"calcId,attr"`
|
||||
CalcMode *string `xml:"calcMode,attr"`
|
||||
FullCalcOnLoad *bool `xml:"fullCalcOnLoad,attr"`
|
||||
RefMode *string `xml:"refMode,attr"`
|
||||
Iterate *bool `xml:"iterate,attr"`
|
||||
IterateCount *uint `xml:"iterateCount,attr"`
|
||||
IterateDelta *float64 `xml:"iterateDelta,attr"`
|
||||
FullPrecision *bool `xml:"fullPrecision,attr"`
|
||||
CalcCompleted *bool `xml:"calcCompleted,attr"`
|
||||
CalcOnSave *bool `xml:"calcOnSave,attr"`
|
||||
ConcurrentCalc *bool `xml:"concurrentCalc,attr"`
|
||||
ConcurrentManualCount *uint `xml:"concurrentManualCount,attr"`
|
||||
ForceFullCalc *bool `xml:"forceFullCalc,attr"`
|
||||
}
|
||||
|
||||
// WorkbookPropsOptions directly maps the settings of workbook proprieties.
|
||||
type WorkbookPropsOptions struct {
|
||||
Date1904 *bool
|
||||
|
|
Loading…
Reference in New Issue