diff options
author | Veniamin Albaev <albenik@gmail.com> | 2019-03-19 19:14:41 +0300 |
---|---|---|
committer | xuri <xuri.me@gmail.com> | 2019-03-20 00:14:41 +0800 |
commit | dc01264562e6e88d77a28042408029770ea32df4 (patch) | |
tree | f3d8fd1627fb71676bab59fe2fa1c9b076b360d8 /rows.go | |
parent | 092f16c744c40e85be5cf6128dfb35c96e7df78b (diff) |
Huge refactorig for consistent col/row numbering (#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
Diffstat (limited to 'rows.go')
-rw-r--r-- | rows.go | 197 |
1 files changed, 91 insertions, 106 deletions
@@ -16,7 +16,6 @@ import ( "io" "math" "strconv" - "strings" ) // GetRows return all the rows in a sheet by given worksheet name (case @@ -30,24 +29,30 @@ import ( // } // func (f *File) GetRows(sheet string) [][]string { - xlsx := f.workSheetReader(sheet) name, ok := f.sheetMap[trimSheetName(sheet)] if !ok { - return [][]string{} + return nil } + + xlsx := f.workSheetReader(sheet) if xlsx != nil { output, _ := xml.Marshal(f.Sheet[name]) f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpaceBytes(output)) } + xml.NewDecoder(bytes.NewReader(f.readXML(name))) d := f.sharedStringsReader() - var inElement string - var r xlsxRow - tr, tc := f.getTotalRowsCols(name) - rows := make([][]string, tr) + var ( + inElement string + rowData xlsxRow + ) + + rowCount, colCount := f.getTotalRowsCols(name) + rows := make([][]string, rowCount) for i := range rows { - rows[i] = make([]string, tc+1) + rows[i] = make([]string, colCount+1) } + var row int decoder := xml.NewDecoder(bytes.NewReader(f.readXML(name))) for { @@ -59,15 +64,15 @@ func (f *File) GetRows(sheet string) [][]string { case xml.StartElement: inElement = startElement.Name.Local if inElement == "row" { - r = xlsxRow{} - _ = decoder.DecodeElement(&r, &startElement) - cr := r.R - 1 - for _, colCell := range r.C { - c := TitleToNumber(strings.Map(letterOnlyMapF, colCell.R)) + rowData = xlsxRow{} + _ = decoder.DecodeElement(&rowData, &startElement) + cr := rowData.R - 1 + for _, colCell := range rowData.C { + col, _ := MustCellNameToCoordinates(colCell.R) val, _ := colCell.getValueFrom(f, d) - rows[cr][c] = val + rows[cr][col-1] = val if val != "" { - row = r.R + row = rowData.R } } } @@ -120,13 +125,13 @@ func (rows *Rows) Columns() []string { r := xlsxRow{} _ = rows.decoder.DecodeElement(&r, &startElement) d := rows.f.sharedStringsReader() - row := make([]string, len(r.C)) + columns := make([]string, len(r.C)) for _, colCell := range r.C { - c := TitleToNumber(strings.Map(letterOnlyMapF, colCell.R)) + col, _ := MustCellNameToCoordinates(colCell.R) val, _ := colCell.getValueFrom(rows.f, d) - row[c] = val + columns[col-1] = val } - return row + return columns } // ErrSheetNotExist defines an error of sheet is not exist @@ -184,7 +189,7 @@ func (f *File) getTotalRowsCols(name string) (int, int) { _ = decoder.DecodeElement(&r, &startElement) tr = r.R for _, colCell := range r.C { - col := TitleToNumber(strings.Map(letterOnlyMapF, colCell.R)) + col, _ := MustCellNameToCoordinates(colCell.R) if col > tc { tc = col } @@ -202,13 +207,15 @@ func (f *File) getTotalRowsCols(name string) (int, int) { // xlsx.SetRowHeight("Sheet1", 1, 50) // func (f *File) SetRowHeight(sheet string, row int, height float64) { - xlsx := f.workSheetReader(sheet) if row < 1 { - return + panic(newInvalidRowNumberError(row)) // Fail fats to avoid possible future side effects! } - cells := 0 + + xlsx := f.workSheetReader(sheet) + + prepareSheetXML(xlsx, 0, row) + rowIdx := row - 1 - completeRow(xlsx, row, cells) xlsx.SheetData.Row[rowIdx].Ht = height xlsx.SheetData.Row[rowIdx].CustomHeight = true } @@ -232,8 +239,12 @@ func (f *File) getRowHeight(sheet string, row int) int { // xlsx.GetRowHeight("Sheet1", 1) // func (f *File) GetRowHeight(sheet string, row int) float64 { + if row < 1 { + panic(newInvalidRowNumberError(row)) // Fail fats to avoid possible future side effects! + } + xlsx := f.workSheetReader(sheet) - if row < 1 || row > len(xlsx.SheetData.Row) { + if row > len(xlsx.SheetData.Row) { return defaultRowHeightPixels // it will be better to use 0, but we take care with BC } for _, v := range xlsx.SheetData.Row { @@ -291,18 +302,13 @@ func (xlsx *xlsxC) getValueFrom(f *File, d *xlsxSST) (string, error) { // xlsx.SetRowVisible("Sheet1", 2, false) // func (f *File) SetRowVisible(sheet string, row int, visible bool) { - xlsx := f.workSheetReader(sheet) if row < 1 { - return - } - cells := 0 - completeRow(xlsx, row, cells) - rowIdx := row - 1 - if visible { - xlsx.SheetData.Row[rowIdx].Hidden = false - return + panic(newInvalidRowNumberError(row)) // Fail fats to avoid possible future side effects! } - xlsx.SheetData.Row[rowIdx].Hidden = true + + xlsx := f.workSheetReader(sheet) + prepareSheetXML(xlsx, 0, row) + xlsx.SheetData.Row[row-1].Hidden = !visible } // GetRowVisible provides a function to get visible of a single row by given @@ -312,14 +318,15 @@ func (f *File) SetRowVisible(sheet string, row int, visible bool) { // xlsx.GetRowVisible("Sheet1", 2) // func (f *File) GetRowVisible(sheet string, row int) bool { + if row < 1 { + panic(newInvalidRowNumberError(row)) // Fail fats to avoid possible future side effects! + } + xlsx := f.workSheetReader(sheet) - if row < 1 || row > len(xlsx.SheetData.Row) { + if row > len(xlsx.SheetData.Row) { return false } - rowIndex := row - 1 - cells := 0 - completeRow(xlsx, row, cells) - return !xlsx.SheetData.Row[rowIndex].Hidden + return !xlsx.SheetData.Row[row-1].Hidden } // SetRowOutlineLevel provides a function to set outline level number of a @@ -329,12 +336,11 @@ func (f *File) GetRowVisible(sheet string, row int) bool { // xlsx.SetRowOutlineLevel("Sheet1", 2, 1) // func (f *File) SetRowOutlineLevel(sheet string, row int, level uint8) { - xlsx := f.workSheetReader(sheet) if row < 1 { - return + panic(newInvalidRowNumberError(row)) // Fail fats to avoid possible future side effects! } - cells := 0 - completeRow(xlsx, row, cells) + xlsx := f.workSheetReader(sheet) + prepareSheetXML(xlsx, 0, row) xlsx.SheetData.Row[row-1].OutlineLevel = level } @@ -345,8 +351,11 @@ func (f *File) SetRowOutlineLevel(sheet string, row int, level uint8) { // xlsx.GetRowOutlineLevel("Sheet1", 2) // func (f *File) GetRowOutlineLevel(sheet string, row int) uint8 { + if row < 1 { + panic(newInvalidRowNumberError(row)) // Fail fats to avoid possible future side effects! + } xlsx := f.workSheetReader(sheet) - if row < 1 || row > len(xlsx.SheetData.Row) { + if row > len(xlsx.SheetData.Row) { return 0 } return xlsx.SheetData.Row[row-1].OutlineLevel @@ -362,14 +371,18 @@ func (f *File) GetRowOutlineLevel(sheet string, row int) uint8 { // worksheet, it will cause a file error when you open it. The excelize only // partially updates these references currently. func (f *File) RemoveRow(sheet string, row int) { + if row < 1 { + panic(newInvalidRowNumberError(row)) // Fail fats to avoid possible future side effects! + } + xlsx := f.workSheetReader(sheet) - if row < 1 || row > len(xlsx.SheetData.Row) { + if row > len(xlsx.SheetData.Row) { return } for i, r := range xlsx.SheetData.Row { if r.R == row { xlsx.SheetData.Row = append(xlsx.SheetData.Row[:i], xlsx.SheetData.Row[i+1:]...) - f.adjustHelper(sheet, -1, row, -1) + f.adjustHelper(sheet, rows, row, -1) return } } @@ -383,9 +396,9 @@ func (f *File) RemoveRow(sheet string, row int) { // func (f *File) InsertRow(sheet string, row int) { if row < 1 { - return + panic(newInvalidRowNumberError(row)) // Fail fats to avoid possible future side effects! } - f.adjustHelper(sheet, -1, row, 1) + f.adjustHelper(sheet, rows, row, 1) } // DuplicateRow inserts a copy of specified row (by it Excel row number) below @@ -410,9 +423,12 @@ func (f *File) DuplicateRow(sheet string, row int) { // worksheet, it will cause a file error when you open it. The excelize only // partially updates these references currently. func (f *File) DuplicateRowTo(sheet string, row, row2 int) { - xlsx := f.workSheetReader(sheet) + if row < 1 { + panic(newInvalidRowNumberError(row)) // Fail fats to avoid possible future side effects! + } - if row < 1 || row > len(xlsx.SheetData.Row) || row2 < 1 || row == row2 { + xlsx := f.workSheetReader(sheet) + if row > len(xlsx.SheetData.Row) || row2 < 1 || row == row2 { return } @@ -430,7 +446,7 @@ func (f *File) DuplicateRowTo(sheet string, row, row2 int) { return } - f.adjustHelper(sheet, -1, row2, 1) + f.adjustHelper(sheet, rows, row2, 1) idx2 := -1 for i, r := range xlsx.SheetData.Row { @@ -478,62 +494,31 @@ func (f *File) DuplicateRowTo(sheet string, row, row2 int) { // Noteice: this method could be very slow for large spreadsheets (more than // 3000 rows one sheet). func checkRow(xlsx *xlsxWorksheet) { - buffer := bytes.Buffer{} - for k := range xlsx.SheetData.Row { - lenCol := len(xlsx.SheetData.Row[k].C) - if lenCol > 0 { - endR := string(strings.Map(letterOnlyMapF, xlsx.SheetData.Row[k].C[lenCol-1].R)) - endRow, _ := strconv.Atoi(strings.Map(intOnlyMapF, xlsx.SheetData.Row[k].C[lenCol-1].R)) - endCol := TitleToNumber(endR) + 1 - if lenCol < endCol { - oldRow := xlsx.SheetData.Row[k].C - xlsx.SheetData.Row[k].C = xlsx.SheetData.Row[k].C[:0] - var tmp []xlsxC - for i := 0; i < endCol; i++ { - buffer.WriteString(ToAlphaString(i)) - buffer.WriteString(strconv.Itoa(endRow)) - tmp = append(tmp, xlsxC{ - R: buffer.String(), - }) - buffer.Reset() - } - xlsx.SheetData.Row[k].C = tmp - for _, y := range oldRow { - colAxis := TitleToNumber(string(strings.Map(letterOnlyMapF, y.R))) - xlsx.SheetData.Row[k].C[colAxis] = y - } - } - } - } -} + for rowIdx := range xlsx.SheetData.Row { + rowData := &xlsx.SheetData.Row[rowIdx] -// completeRow provides a function to check and fill each column element for a -// single row and make that is continuous in a worksheet of XML by given row -// index and axis. -func completeRow(xlsx *xlsxWorksheet, row, cell int) { - currentRows := len(xlsx.SheetData.Row) - if currentRows > 1 { - lastRow := xlsx.SheetData.Row[currentRows-1].R - if lastRow >= row { - row = lastRow + colCount := len(rowData.C) + if colCount == 0 { + continue } - } - for i := currentRows; i < row; i++ { - xlsx.SheetData.Row = append(xlsx.SheetData.Row, xlsxRow{ - R: i + 1, - }) - } - buffer := bytes.Buffer{} - for ii := currentRows; ii < row; ii++ { - start := len(xlsx.SheetData.Row[ii].C) - if start == 0 { - for iii := start; iii < cell; iii++ { - buffer.WriteString(ToAlphaString(iii)) - buffer.WriteString(strconv.Itoa(ii + 1)) - xlsx.SheetData.Row[ii].C = append(xlsx.SheetData.Row[ii].C, xlsxC{ - R: buffer.String(), - }) - buffer.Reset() + lastCol, _ := MustCellNameToCoordinates(rowData.C[colCount-1].R) + + if colCount < lastCol { + oldList := rowData.C + newlist := make([]xlsxC, 0, lastCol) + + rowData.C = xlsx.SheetData.Row[rowIdx].C[:0] + + for colIdx := 0; colIdx < lastCol; colIdx++ { + newlist = append(newlist, xlsxC{R: MustCoordinatesToCellName(colIdx+1, rowIdx+1)}) + } + + rowData.C = newlist + + for colIdx := range oldList { + colData := &oldList[colIdx] + colNum, _ := MustCellNameToCoordinates(colData.R) + xlsx.SheetData.Row[rowIdx].C[colNum-1] = *colData } } } |