Skip to content

Commit aef20e2

Browse files
romanshevelevxuri
authored andcommitted
Introduce 2 new functions SetCalcProps and GetCalcProps (qax-os#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
1 parent c6d161f commit aef20e2

File tree

6 files changed

+182
-81
lines changed

6 files changed

+182
-81
lines changed

docProps.go

+11-46
Original file line numberDiff line numberDiff line change
@@ -65,34 +65,15 @@ import (
6565
// AppVersion: "16.0000",
6666
// })
6767
func (f *File) SetAppProps(appProperties *AppProperties) error {
68-
var (
69-
app *xlsxProperties
70-
err error
71-
field string
72-
fields []string
73-
immutable, mutable reflect.Value
74-
output []byte
75-
)
76-
app = new(xlsxProperties)
77-
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLPathDocPropsApp)))).
68+
app := new(xlsxProperties)
69+
if err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLPathDocPropsApp)))).
7870
Decode(app); err != nil && err != io.EOF {
7971
return err
8072
}
81-
fields = []string{"Application", "ScaleCrop", "DocSecurity", "Company", "LinksUpToDate", "HyperlinksChanged", "AppVersion"}
82-
immutable, mutable = reflect.ValueOf(*appProperties), reflect.ValueOf(app).Elem()
83-
for _, field = range fields {
84-
immutableField := immutable.FieldByName(field)
85-
switch immutableField.Kind() {
86-
case reflect.Bool:
87-
mutable.FieldByName(field).SetBool(immutableField.Bool())
88-
case reflect.Int:
89-
mutable.FieldByName(field).SetInt(immutableField.Int())
90-
default:
91-
mutable.FieldByName(field).SetString(immutableField.String())
92-
}
93-
}
73+
setNoPtrFieldsVal([]string{"Application", "ScaleCrop", "DocSecurity", "Company", "LinksUpToDate", "HyperlinksChanged", "AppVersion"},
74+
reflect.ValueOf(*appProperties), reflect.ValueOf(app).Elem())
9475
app.Vt = NameSpaceDocumentPropertiesVariantTypes.Value
95-
output, err = xml.Marshal(app)
76+
output, err := xml.Marshal(app)
9677
f.saveFileList(defaultXMLPathDocPropsApp, output)
9778
return err
9879
}
@@ -180,22 +161,12 @@ func (f *File) GetAppProps() (ret *AppProperties, err error) {
180161
// Version: "1.0.0",
181162
// })
182163
func (f *File) SetDocProps(docProperties *DocProperties) error {
183-
var (
184-
core *decodeCoreProperties
185-
err error
186-
field, val string
187-
fields []string
188-
immutable, mutable reflect.Value
189-
newProps *xlsxCoreProperties
190-
output []byte
191-
)
192-
193-
core = new(decodeCoreProperties)
194-
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLPathDocPropsCore)))).
164+
core := new(decodeCoreProperties)
165+
if err := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(defaultXMLPathDocPropsCore)))).
195166
Decode(core); err != nil && err != io.EOF {
196167
return err
197168
}
198-
newProps = &xlsxCoreProperties{
169+
newProps := &xlsxCoreProperties{
199170
Dc: NameSpaceDublinCore,
200171
Dcterms: NameSpaceDublinCoreTerms,
201172
Dcmitype: NameSpaceDublinCoreMetadataInitiative,
@@ -219,23 +190,17 @@ func (f *File) SetDocProps(docProperties *DocProperties) error {
219190
if core.Modified != nil {
220191
newProps.Modified = &xlsxDcTerms{Type: core.Modified.Type, Text: core.Modified.Text}
221192
}
222-
fields = []string{
193+
setNoPtrFieldsVal([]string{
223194
"Category", "ContentStatus", "Creator", "Description", "Identifier", "Keywords",
224195
"LastModifiedBy", "Revision", "Subject", "Title", "Language", "Version",
225-
}
226-
immutable, mutable = reflect.ValueOf(*docProperties), reflect.ValueOf(newProps).Elem()
227-
for _, field = range fields {
228-
if val = immutable.FieldByName(field).String(); val != "" {
229-
mutable.FieldByName(field).SetString(val)
230-
}
231-
}
196+
}, reflect.ValueOf(*docProperties), reflect.ValueOf(newProps).Elem())
232197
if docProperties.Created != "" {
233198
newProps.Created = &xlsxDcTerms{Type: "dcterms:W3CDTF", Text: docProperties.Created}
234199
}
235200
if docProperties.Modified != "" {
236201
newProps.Modified = &xlsxDcTerms{Type: "dcterms:W3CDTF", Text: docProperties.Modified}
237202
}
238-
output, err = xml.Marshal(newProps)
203+
output, err := xml.Marshal(newProps)
239204
f.saveFileList(defaultXMLPathDocPropsCore, output)
240205

241206
return err

lib.go

+49
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"math"
2222
"math/big"
2323
"os"
24+
"reflect"
2425
"regexp"
2526
"strconv"
2627
"strings"
@@ -878,6 +879,54 @@ func continuedFraction(n float64, i int64, limit int64, prec float64) *big.Rat {
878879
return res
879880
}
880881

882+
// assignFieldValue assigns the value from an immutable reflect.Value to a
883+
// mutable reflect.Value based on the type of the immutable value.
884+
func assignFieldValue(field string, immutable, mutable reflect.Value) {
885+
switch immutable.Kind() {
886+
case reflect.Bool:
887+
mutable.FieldByName(field).SetBool(immutable.Bool())
888+
case reflect.Int:
889+
mutable.FieldByName(field).SetInt(immutable.Int())
890+
default:
891+
mutable.FieldByName(field).SetString(immutable.String())
892+
}
893+
}
894+
895+
// setNoPtrFieldsVal assigns values from the pointer or no-pointer structs
896+
// fields (immutable) value to no-pointer struct field.
897+
func setNoPtrFieldsVal(fields []string, immutable, mutable reflect.Value) {
898+
for _, field := range fields {
899+
immutableField := immutable.FieldByName(field)
900+
if immutableField.Kind() == reflect.Ptr {
901+
if immutableField.IsValid() && !immutableField.IsNil() {
902+
assignFieldValue(field, immutableField.Elem(), mutable)
903+
}
904+
continue
905+
}
906+
assignFieldValue(field, immutableField, mutable)
907+
}
908+
}
909+
910+
// setPtrFieldsVal assigns values from the pointer or no-pointer structs
911+
// fields (immutable) value to pointer struct field.
912+
func setPtrFieldsVal(fields []string, immutable, mutable reflect.Value) {
913+
for _, field := range fields {
914+
immutableField := immutable.FieldByName(field)
915+
if immutableField.Kind() == reflect.Ptr {
916+
if immutableField.IsValid() && !immutableField.IsNil() {
917+
mutable.FieldByName(field).Set(immutableField.Elem())
918+
}
919+
continue
920+
}
921+
if immutableField.IsZero() {
922+
continue
923+
}
924+
ptr := reflect.New(immutableField.Type())
925+
ptr.Elem().Set(immutableField)
926+
mutable.FieldByName(field).Set(ptr)
927+
}
928+
}
929+
881930
// Stack defined an abstract data type that serves as a collection of elements.
882931
type Stack struct {
883932
list *list.List

pivotTable.go

+6-19
Original file line numberDiff line numberDiff line change
@@ -883,14 +883,10 @@ func (f *File) getPivotTable(sheet, pivotTableXML, pivotCacheRels string) (Pivot
883883
opts.DataRange = pc.CacheSource.WorksheetSource.Name
884884
_ = f.getPivotTableDataRange(&opts)
885885
}
886-
fields := []string{"RowGrandTotals", "ColGrandTotals", "ShowDrill", "UseAutoFormatting", "PageOverThenDown", "MergeItem", "CompactData", "ShowError"}
887-
immutable, mutable := reflect.ValueOf(*pt), reflect.ValueOf(&opts).Elem()
888-
for _, field := range fields {
889-
immutableField := immutable.FieldByName(field)
890-
if immutableField.Kind() == reflect.Ptr && !immutableField.IsNil() && immutableField.Elem().Kind() == reflect.Bool {
891-
mutable.FieldByName(field).SetBool(immutableField.Elem().Bool())
892-
}
893-
}
886+
setPtrFieldsVal([]string{
887+
"RowGrandTotals", "ColGrandTotals", "ShowDrill",
888+
"UseAutoFormatting", "PageOverThenDown", "MergeItem", "CompactData", "ShowError",
889+
}, reflect.ValueOf(*pt), reflect.ValueOf(&opts).Elem())
894890
if si := pt.PivotTableStyleInfo; si != nil {
895891
opts.ShowRowHeaders = si.ShowRowHeaders
896892
opts.ShowColHeaders = si.ShowColHeaders
@@ -982,17 +978,8 @@ func extractPivotTableField(data string, fld *xlsxPivotField) PivotTableField {
982978
ShowAll: fld.ShowAll,
983979
InsertBlankRow: fld.InsertBlankRow,
984980
}
985-
fields := []string{"Compact", "Name", "Outline", "Subtotal", "DefaultSubtotal"}
986-
immutable, mutable := reflect.ValueOf(*fld), reflect.ValueOf(&pivotTableField).Elem()
987-
for _, field := range fields {
988-
immutableField := immutable.FieldByName(field)
989-
if immutableField.Kind() == reflect.String {
990-
mutable.FieldByName(field).SetString(immutableField.String())
991-
}
992-
if immutableField.Kind() == reflect.Ptr && !immutableField.IsNil() && immutableField.Elem().Kind() == reflect.Bool {
993-
mutable.FieldByName(field).SetBool(immutableField.Elem().Bool())
994-
}
995-
}
981+
setPtrFieldsVal([]string{"Compact", "Name", "Outline", "DefaultSubtotal"},
982+
reflect.ValueOf(*fld), reflect.ValueOf(&pivotTableField).Elem())
996983
return pivotTableField
997984
}
998985

workbook.go

+61-15
Original file line numberDiff line numberDiff line change
@@ -16,46 +16,92 @@ import (
1616
"encoding/xml"
1717
"io"
1818
"path/filepath"
19+
"reflect"
1920
"strconv"
2021
"strings"
2122
)
2223

2324
// SetWorkbookProps provides a function to sets workbook properties.
2425
func (f *File) SetWorkbookProps(opts *WorkbookPropsOptions) error {
26+
if opts == nil {
27+
return nil
28+
}
2529
wb, err := f.workbookReader()
2630
if err != nil {
2731
return err
2832
}
2933
if wb.WorkbookPr == nil {
3034
wb.WorkbookPr = new(xlsxWorkbookPr)
3135
}
36+
setNoPtrFieldsVal([]string{"Date1904", "FilterPrivacy", "CodeName"},
37+
reflect.ValueOf(*opts), reflect.ValueOf(wb.WorkbookPr).Elem())
38+
return err
39+
}
40+
41+
// GetWorkbookProps provides a function to gets workbook properties.
42+
func (f *File) GetWorkbookProps() (WorkbookPropsOptions, error) {
43+
var opts WorkbookPropsOptions
44+
wb, err := f.workbookReader()
45+
if err != nil {
46+
return opts, err
47+
}
48+
if wb.WorkbookPr == nil {
49+
return opts, err
50+
}
51+
setPtrFieldsVal([]string{"Date1904", "FilterPrivacy", "CodeName"},
52+
reflect.ValueOf(*wb.WorkbookPr), reflect.ValueOf(&opts).Elem())
53+
return opts, err
54+
}
55+
56+
// SetCalcProps provides a function to sets calculation properties.
57+
func (f *File) SetCalcProps(opts *CalcPropsOptions) error {
3258
if opts == nil {
3359
return nil
3460
}
35-
if opts.Date1904 != nil {
36-
wb.WorkbookPr.Date1904 = *opts.Date1904
61+
wb, err := f.workbookReader()
62+
if err != nil {
63+
return err
64+
}
65+
if wb.CalcPr == nil {
66+
wb.CalcPr = new(xlsxCalcPr)
3767
}
38-
if opts.FilterPrivacy != nil {
39-
wb.WorkbookPr.FilterPrivacy = *opts.FilterPrivacy
68+
setNoPtrFieldsVal([]string{
69+
"CalcCompleted", "CalcOnSave", "ForceFullCalc", "FullCalcOnLoad", "FullPrecision", "Iterate",
70+
"IterateDelta",
71+
"CalcMode", "RefMode",
72+
}, reflect.ValueOf(*opts), reflect.ValueOf(wb.CalcPr).Elem())
73+
if opts.CalcID != nil {
74+
wb.CalcPr.CalcID = int(*opts.CalcID)
4075
}
41-
if opts.CodeName != nil {
42-
wb.WorkbookPr.CodeName = *opts.CodeName
76+
if opts.ConcurrentManualCount != nil {
77+
wb.CalcPr.ConcurrentManualCount = int(*opts.ConcurrentManualCount)
4378
}
44-
return nil
79+
if opts.IterateCount != nil {
80+
wb.CalcPr.IterateCount = int(*opts.IterateCount)
81+
}
82+
wb.CalcPr.ConcurrentCalc = opts.ConcurrentCalc
83+
return err
4584
}
4685

47-
// GetWorkbookProps provides a function to gets workbook properties.
48-
func (f *File) GetWorkbookProps() (WorkbookPropsOptions, error) {
49-
var opts WorkbookPropsOptions
86+
// GetCalcProps provides a function to gets calculation properties.
87+
func (f *File) GetCalcProps() (CalcPropsOptions, error) {
88+
var opts CalcPropsOptions
5089
wb, err := f.workbookReader()
5190
if err != nil {
5291
return opts, err
5392
}
54-
if wb.WorkbookPr != nil {
55-
opts.Date1904 = boolPtr(wb.WorkbookPr.Date1904)
56-
opts.FilterPrivacy = boolPtr(wb.WorkbookPr.FilterPrivacy)
57-
opts.CodeName = stringPtr(wb.WorkbookPr.CodeName)
93+
if wb.CalcPr == nil {
94+
return opts, err
5895
}
96+
setPtrFieldsVal([]string{
97+
"CalcCompleted", "CalcOnSave", "ForceFullCalc", "FullCalcOnLoad", "FullPrecision", "Iterate",
98+
"IterateDelta",
99+
"CalcMode", "RefMode",
100+
}, reflect.ValueOf(*wb.CalcPr), reflect.ValueOf(&opts).Elem())
101+
opts.CalcID = uintPtr(uint(wb.CalcPr.CalcID))
102+
opts.ConcurrentManualCount = uintPtr(uint(wb.CalcPr.ConcurrentManualCount))
103+
opts.IterateCount = uintPtr(uint(wb.CalcPr.IterateCount))
104+
opts.ConcurrentCalc = wb.CalcPr.ConcurrentCalc
59105
return opts, err
60106
}
61107

@@ -99,7 +145,7 @@ func (f *File) ProtectWorkbook(opts *WorkbookProtectionOptions) error {
99145
wb.WorkbookProtection.WorkbookHashValue = hashValue
100146
wb.WorkbookProtection.WorkbookSpinCount = int(workbookProtectionSpinCount)
101147
}
102-
return nil
148+
return err
103149
}
104150

105151
// UnprotectWorkbook provides a function to remove protection for workbook,

workbook_test.go

+36
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ func TestWorkbookProps(t *testing.T) {
2121
opts, err := f.GetWorkbookProps()
2222
assert.NoError(t, err)
2323
assert.Equal(t, expected, opts)
24+
wb.WorkbookPr = nil
25+
opts, err = f.GetWorkbookProps()
26+
assert.NoError(t, err)
27+
assert.Equal(t, WorkbookPropsOptions{}, opts)
2428
// Test set workbook properties with unsupported charset workbook
2529
f.WorkBook = nil
2630
f.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
@@ -32,6 +36,38 @@ func TestWorkbookProps(t *testing.T) {
3236
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
3337
}
3438

39+
func TestCalcProps(t *testing.T) {
40+
f := NewFile()
41+
assert.NoError(t, f.SetCalcProps(nil))
42+
wb, err := f.workbookReader()
43+
assert.NoError(t, err)
44+
wb.CalcPr = nil
45+
expected := CalcPropsOptions{
46+
FullCalcOnLoad: boolPtr(true),
47+
CalcID: uintPtr(122211),
48+
ConcurrentManualCount: uintPtr(5),
49+
IterateCount: uintPtr(10),
50+
ConcurrentCalc: boolPtr(true),
51+
}
52+
assert.NoError(t, f.SetCalcProps(&expected))
53+
opts, err := f.GetCalcProps()
54+
assert.NoError(t, err)
55+
assert.Equal(t, expected, opts)
56+
wb.CalcPr = nil
57+
opts, err = f.GetCalcProps()
58+
assert.NoError(t, err)
59+
assert.Equal(t, CalcPropsOptions{}, opts)
60+
// Test set workbook properties with unsupported charset workbook
61+
f.WorkBook = nil
62+
f.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
63+
assert.EqualError(t, f.SetCalcProps(&expected), "XML syntax error on line 1: invalid UTF-8")
64+
// Test get workbook properties with unsupported charset workbook
65+
f.WorkBook = nil
66+
f.Pkg.Store(defaultXMLPathWorkbook, MacintoshCyrillicCharset)
67+
_, err = f.GetCalcProps()
68+
assert.EqualError(t, err, "XML syntax error on line 1: invalid UTF-8")
69+
}
70+
3571
func TestDeleteWorkbookRels(t *testing.T) {
3672
f := NewFile()
3773
// Test delete pivot table without worksheet relationships

xmlWorkbook.go

+19-1
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ type xlsxDefinedName struct {
315315
// displaying the results as values in the cells that contain the formulas.
316316
type xlsxCalcPr struct {
317317
CalcCompleted bool `xml:"calcCompleted,attr,omitempty"`
318-
CalcID string `xml:"calcId,attr,omitempty"`
318+
CalcID int `xml:"calcId,attr,omitempty"`
319319
CalcMode string `xml:"calcMode,attr,omitempty"`
320320
CalcOnSave bool `xml:"calcOnSave,attr,omitempty"`
321321
ConcurrentCalc *bool `xml:"concurrentCalc,attr"`
@@ -384,6 +384,24 @@ type DefinedName struct {
384384
Scope string
385385
}
386386

387+
// CalcPropsOptions defines the collection of properties the application uses to
388+
// record calculation status and details.
389+
type CalcPropsOptions struct {
390+
CalcID *uint `xml:"calcId,attr"`
391+
CalcMode *string `xml:"calcMode,attr"`
392+
FullCalcOnLoad *bool `xml:"fullCalcOnLoad,attr"`
393+
RefMode *string `xml:"refMode,attr"`
394+
Iterate *bool `xml:"iterate,attr"`
395+
IterateCount *uint `xml:"iterateCount,attr"`
396+
IterateDelta *float64 `xml:"iterateDelta,attr"`
397+
FullPrecision *bool `xml:"fullPrecision,attr"`
398+
CalcCompleted *bool `xml:"calcCompleted,attr"`
399+
CalcOnSave *bool `xml:"calcOnSave,attr"`
400+
ConcurrentCalc *bool `xml:"concurrentCalc,attr"`
401+
ConcurrentManualCount *uint `xml:"concurrentManualCount,attr"`
402+
ForceFullCalc *bool `xml:"forceFullCalc,attr"`
403+
}
404+
387405
// WorkbookPropsOptions directly maps the settings of workbook proprieties.
388406
type WorkbookPropsOptions struct {
389407
Date1904 *bool

0 commit comments

Comments
 (0)