Skip to content

Commit dc01264

Browse files
albenikxuri
authored andcommitted
Huge refactorig for consistent col/row numbering (qax-os#356)
* Huge refactorig for consistent col/row numbering Started from simply changing ToALphaString()/TitleToNumber() logic and related fixes. But have to go deeper, do fixes, after do related fixes and again and again. Major improvements: 1. Tests made stronger again (But still be weak). 2. "Empty" returns for incorrect input replaces with panic. 3. Check for correct col/row/cell naming & addressing by default. 4. Removed huge amount of duplicated code. 5. Removed ToALphaString(), TitleToNumber() and it helpers functions at all, and replaced with SplitCellName(), JoinCellName(), ColumnNameToNumber(), ColumnNumberToName(), CellNameToCoordinates(), CoordinatesToCellName(). 6. Minor fixes for internal variable naming for code readability (ex. col, row for input params, colIdx, rowIdx for slice indexes etc). * Formatting fixes
1 parent 092f16c commit dc01264

23 files changed

+1647
-1334
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
~$*.xlsx
12
test/Test*.xlsx

adjust.go

+229
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
package excelize
2+
3+
import (
4+
"strings"
5+
)
6+
7+
type adjustDirection bool
8+
9+
const (
10+
columns adjustDirection = false
11+
rows adjustDirection = true
12+
)
13+
14+
// adjustHelper provides a function to adjust rows and columns dimensions,
15+
// hyperlinks, merged cells and auto filter when inserting or deleting rows or
16+
// columns.
17+
//
18+
// sheet: Worksheet name that we're editing
19+
// column: Index number of the column we're inserting/deleting before
20+
// row: Index number of the row we're inserting/deleting before
21+
// offset: Number of rows/column to insert/delete negative values indicate deletion
22+
//
23+
// TODO: adjustCalcChain, adjustPageBreaks, adjustComments,
24+
// adjustDataValidations, adjustProtectedCells
25+
//
26+
func (f *File) adjustHelper(sheet string, dir adjustDirection, num, offset int) {
27+
xlsx := f.workSheetReader(sheet)
28+
29+
if dir == rows {
30+
f.adjustRowDimensions(xlsx, num, offset)
31+
} else {
32+
f.adjustColDimensions(xlsx, num, offset)
33+
}
34+
f.adjustHyperlinks(xlsx, sheet, dir, num, offset)
35+
f.adjustMergeCells(xlsx, dir, num, offset)
36+
f.adjustAutoFilter(xlsx, dir, num, offset)
37+
38+
checkSheet(xlsx)
39+
checkRow(xlsx)
40+
}
41+
42+
// adjustColDimensions provides a function to update column dimensions when
43+
// inserting or deleting rows or columns.
44+
func (f *File) adjustColDimensions(xlsx *xlsxWorksheet, col, offset int) {
45+
for rowIdx := range xlsx.SheetData.Row {
46+
for colIdx, v := range xlsx.SheetData.Row[rowIdx].C {
47+
cellCol, cellRow, _ := CellNameToCoordinates(v.R)
48+
if col <= cellCol {
49+
if newCol := cellCol + offset; newCol > 0 {
50+
xlsx.SheetData.Row[rowIdx].C[colIdx].R, _ = CoordinatesToCellName(newCol, cellRow)
51+
}
52+
}
53+
}
54+
}
55+
}
56+
57+
// adjustRowDimensions provides a function to update row dimensions when
58+
// inserting or deleting rows or columns.
59+
func (f *File) adjustRowDimensions(xlsx *xlsxWorksheet, row, offset int) {
60+
for i, r := range xlsx.SheetData.Row {
61+
if newRow := r.R + offset; r.R >= row && newRow > 0 {
62+
f.ajustSingleRowDimensions(&xlsx.SheetData.Row[i], newRow)
63+
}
64+
}
65+
}
66+
67+
// ajustSingleRowDimensions provides a function to ajust single row dimensions.
68+
func (f *File) ajustSingleRowDimensions(r *xlsxRow, num int) {
69+
r.R = num
70+
for i, col := range r.C {
71+
colName, _, _ := SplitCellName(col.R)
72+
r.C[i].R, _ = JoinCellName(colName, num)
73+
}
74+
}
75+
76+
// adjustHyperlinks provides a function to update hyperlinks when inserting or
77+
// deleting rows or columns.
78+
func (f *File) adjustHyperlinks(xlsx *xlsxWorksheet, sheet string, dir adjustDirection, num, offset int) {
79+
// short path
80+
if xlsx.Hyperlinks == nil || len(xlsx.Hyperlinks.Hyperlink) == 0 {
81+
return
82+
}
83+
84+
// order is important
85+
if offset < 0 {
86+
for rowIdx, linkData := range xlsx.Hyperlinks.Hyperlink {
87+
colNum, rowNum, _ := CellNameToCoordinates(linkData.Ref)
88+
89+
if (dir == rows && num == rowNum) || (dir == columns && num == colNum) {
90+
f.deleteSheetRelationships(sheet, linkData.RID)
91+
if len(xlsx.Hyperlinks.Hyperlink) > 1 {
92+
xlsx.Hyperlinks.Hyperlink = append(xlsx.Hyperlinks.Hyperlink[:rowIdx],
93+
xlsx.Hyperlinks.Hyperlink[rowIdx+1:]...)
94+
} else {
95+
xlsx.Hyperlinks = nil
96+
}
97+
}
98+
}
99+
}
100+
101+
if xlsx.Hyperlinks == nil {
102+
return
103+
}
104+
105+
for i := range xlsx.Hyperlinks.Hyperlink {
106+
link := &xlsx.Hyperlinks.Hyperlink[i] // get reference
107+
colNum, rowNum, _ := CellNameToCoordinates(link.Ref)
108+
109+
if dir == rows {
110+
if rowNum >= num {
111+
link.Ref, _ = CoordinatesToCellName(colNum, rowNum+offset)
112+
}
113+
} else {
114+
if colNum >= num {
115+
link.Ref, _ = CoordinatesToCellName(colNum+offset, rowNum)
116+
}
117+
}
118+
}
119+
}
120+
121+
// adjustAutoFilter provides a function to update the auto filter when
122+
// inserting or deleting rows or columns.
123+
func (f *File) adjustAutoFilter(xlsx *xlsxWorksheet, dir adjustDirection, num, offset int) {
124+
if xlsx.AutoFilter == nil {
125+
return
126+
}
127+
128+
rng := strings.Split(xlsx.AutoFilter.Ref, ":")
129+
firstCell := rng[0]
130+
lastCell := rng[1]
131+
132+
firstCol, firstRow, err := CellNameToCoordinates(firstCell)
133+
if err != nil {
134+
panic(err)
135+
}
136+
137+
lastCol, lastRow, err := CellNameToCoordinates(lastCell)
138+
if err != nil {
139+
panic(err)
140+
}
141+
142+
if (dir == rows && firstRow == num && offset < 0) || (dir == columns && firstCol == num && lastCol == num) {
143+
xlsx.AutoFilter = nil
144+
for rowIdx := range xlsx.SheetData.Row {
145+
rowData := &xlsx.SheetData.Row[rowIdx]
146+
if rowData.R > firstRow && rowData.R <= lastRow {
147+
rowData.Hidden = false
148+
}
149+
}
150+
return
151+
}
152+
153+
if dir == rows {
154+
if firstRow >= num {
155+
firstCell, _ = CoordinatesToCellName(firstCol, firstRow+offset)
156+
}
157+
if lastRow >= num {
158+
lastCell, _ = CoordinatesToCellName(lastCol, lastRow+offset)
159+
}
160+
} else {
161+
if lastCol >= num {
162+
lastCell, _ = CoordinatesToCellName(lastCol+offset, lastRow)
163+
}
164+
}
165+
166+
xlsx.AutoFilter.Ref = firstCell + ":" + lastCell
167+
}
168+
169+
// adjustMergeCells provides a function to update merged cells when inserting
170+
// or deleting rows or columns.
171+
func (f *File) adjustMergeCells(xlsx *xlsxWorksheet, dir adjustDirection, num, offset int) {
172+
if xlsx.MergeCells == nil {
173+
return
174+
}
175+
176+
for i, areaData := range xlsx.MergeCells.Cells {
177+
rng := strings.Split(areaData.Ref, ":")
178+
firstCell := rng[0]
179+
lastCell := rng[1]
180+
181+
firstCol, firstRow, err := CellNameToCoordinates(firstCell)
182+
if err != nil {
183+
panic(err)
184+
}
185+
186+
lastCol, lastRow, err := CellNameToCoordinates(lastCell)
187+
if err != nil {
188+
panic(err)
189+
}
190+
191+
adjust := func(v int) int {
192+
if v >= num {
193+
v += offset
194+
if v < 1 {
195+
return 1
196+
}
197+
return v
198+
}
199+
return v
200+
}
201+
202+
if dir == rows {
203+
firstRow = adjust(firstRow)
204+
lastRow = adjust(lastRow)
205+
} else {
206+
firstCol = adjust(firstCol)
207+
lastCol = adjust(lastCol)
208+
}
209+
210+
if firstCol == lastCol && firstRow == lastRow {
211+
if len(xlsx.MergeCells.Cells) > 1 {
212+
xlsx.MergeCells.Cells = append(xlsx.MergeCells.Cells[:i], xlsx.MergeCells.Cells[i+1:]...)
213+
xlsx.MergeCells.Count = len(xlsx.MergeCells.Cells)
214+
} else {
215+
xlsx.MergeCells = nil
216+
}
217+
}
218+
219+
if firstCell, err = CoordinatesToCellName(firstCol, firstRow); err != nil {
220+
panic(err)
221+
}
222+
223+
if lastCell, err = CoordinatesToCellName(lastCol, lastRow); err != nil {
224+
panic(err)
225+
}
226+
227+
areaData.Ref = firstCell + ":" + lastCell
228+
}
229+
}

0 commit comments

Comments
 (0)