forked from tealeg/xlsx
-
Notifications
You must be signed in to change notification settings - Fork 0
/
reftable.go
129 lines (119 loc) · 3.48 KB
/
reftable.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package xlsx
type plainTextOrRichText struct {
plainText string
isRichText bool
richText []RichTextRun
}
type RefTable struct {
indexedStrings []plainTextOrRichText
knownStrings map[string]int
knownRichTexts map[string][]int
isWrite bool
}
// NewSharedStringRefTable creates a new, empty RefTable.
func NewSharedStringRefTable() *RefTable {
rt := RefTable{}
rt.knownStrings = make(map[string]int)
rt.knownRichTexts = make(map[string][]int)
return &rt
}
// MakeSharedStringRefTable takes an xlsxSST struct and converts
// it's contents to an slice of strings used to refer to string values
// by numeric index - this is the model used within XLSX worksheet (a
// numeric reference is stored to a shared cell value).
func MakeSharedStringRefTable(source *xlsxSST) *RefTable {
reftable := NewSharedStringRefTable()
reftable.isWrite = false
for _, si := range source.SI {
if len(si.R) > 0 {
richText := xmlToRichText(si.R)
reftable.AddRichText(richText)
} else {
reftable.AddString(si.T.getText())
}
}
return reftable
}
// makeXlsxSST takes a RefTable and returns and
// equivalent xlsxSST representation.
func (rt *RefTable) makeXLSXSST() xlsxSST {
sst := xlsxSST{}
sst.Count = len(rt.indexedStrings)
sst.UniqueCount = sst.Count
for _, ref := range rt.indexedStrings {
si := xlsxSI{}
if ref.isRichText {
si.R = richTextToXml(ref.richText)
} else {
si.T = &xlsxT{Text: ref.plainText}
}
sst.SI = append(sst.SI, si)
}
return sst
}
// ResolveSharedString looks up a string value or the rich text by numeric index from
// a provided reference table (just a slice of strings in the correct order).
// If the rich text was found, non-empty slice will be returned in richText.
// This function only exists to provide clarity of purpose via it's name.
func (rt *RefTable) ResolveSharedString(index int) (plainText string, richText []RichTextRun) {
ptrt := rt.indexedStrings[index]
if ptrt.isRichText {
richText = ptrt.richText
} else {
plainText = ptrt.plainText
}
return
}
// AddString adds a string to the reference table and return it's
// numeric index. If the string already exists then it simply returns
// the existing index.
func (rt *RefTable) AddString(str string) int {
if rt.isWrite {
index, ok := rt.knownStrings[str]
if ok {
return index
}
}
ptrt := plainTextOrRichText{plainText: str, isRichText: false}
rt.indexedStrings = append(rt.indexedStrings, ptrt)
index := len(rt.indexedStrings) - 1
rt.knownStrings[str] = index
return index
}
// AddRichText adds a set of rich text to the reference table and return it's
// numeric index. If a set of rich text already exists then it simply returns
// the existing index.
func (rt *RefTable) AddRichText(r []RichTextRun) int {
plain := richTextToPlainText(r)
if rt.isWrite {
indices, ok := rt.knownRichTexts[plain]
if ok {
for _, index := range indices {
if areRichTextsEqual(rt.indexedStrings[index].richText, r) {
return index
}
}
}
}
ptrt := plainTextOrRichText{isRichText: true}
ptrt.richText = append(ptrt.richText, r...)
rt.indexedStrings = append(rt.indexedStrings, ptrt)
index := len(rt.indexedStrings) - 1
rt.knownRichTexts[plain] = append(rt.knownRichTexts[plain], index)
return index
}
func areRichTextsEqual(r1 []RichTextRun, r2 []RichTextRun) bool {
if len(r1) != len(r2) {
return false
}
for i, rt1 := range r1 {
rt2 := r2[i]
if !rt1.Equals(&rt2) {
return false
}
}
return true
}
func (rt *RefTable) Length() int {
return len(rt.indexedStrings)
}