Browse Source

Merge pull request #194 from thieupham-wf/shared_formula_absolute_reference

Fix shared formulas with absolute cell references ($)
Geoffrey J. Teale 9 years ago
parent
commit
88830a7cb0
2 changed files with 106 additions and 6 deletions
  1. 53 6
      lib.go
  2. 53 0
      lib_test.go

+ 53 - 6
lib.go

@@ -342,14 +342,14 @@ func formulaForCell(rawcell xlsxC, sharedFormulas map[int]sharedFormula) string
 				var start, end int
 				for end = 0; end < len(orig); end++ {
 					c := orig[end]
-					if c >= 'A' && c <= 'Z' {
+					if c >= 'A' && c <= 'Z' || c == '$' {
 						res += string(orig[start:end])
 						start = end
 						end++
 						foundNum := false
 						for ; end < len(orig); end++ {
 							idc := orig[end]
-							if idc >= '0' && idc <= '9' {
+							if idc >= '0' && idc <= '9' || idc == '$' {
 								foundNum = true
 							} else if idc >= 'A' && idc <= 'Z' {
 								if foundNum {
@@ -360,10 +360,8 @@ func formulaForCell(rawcell xlsxC, sharedFormulas map[int]sharedFormula) string
 							}
 						}
 						if foundNum {
-							fx, fy, _ := getCoordsFromCellIDString(string(orig[start:end]))
-							fx += dx
-							fy += dy
-							res += getCellIDStringFromCoords(fx, fy)
+							cellID := string(orig[start:end])
+							res += shiftCell(cellID, dx, dy)
 							start = end
 						}
 					}
@@ -379,6 +377,55 @@ func formulaForCell(rawcell xlsxC, sharedFormulas map[int]sharedFormula) string
 	return strings.Trim(res, " \t\n\r")
 }
 
+// shiftCell returns the cell shifted according to dx and dy taking into consideration of absolute
+// references with dollar sign ($)
+func shiftCell(cellID string, dx, dy int) string {
+	fx, fy, _ := getCoordsFromCellIDString(cellID)
+
+	// Is fixed column?
+	fixedCol := strings.Index(cellID, "$") == 0
+
+	// Is fixed row?
+	fixedRow := strings.LastIndex(cellID, "$") > 0
+
+	if !fixedCol {
+		// Shift column
+		fx += dx
+	}
+
+	if !fixedRow {
+		// Shift row
+		fy += dy
+	}
+
+	// New shifted cell
+	shiftedCellID := getCellIDStringFromCoords(fx, fy)
+
+	if !fixedCol && !fixedRow {
+		return shiftedCellID
+	}
+
+	// There are absolute references, need to put the $ back into the formula.
+	letterPart := strings.Map(letterOnlyMapF, shiftedCellID)
+	numberPart := strings.Map(intOnlyMapF, shiftedCellID)
+
+	result := ""
+
+	if fixedCol {
+		result += "$"
+	}
+
+	result += letterPart
+
+	if fixedRow {
+		result += "$"
+	}
+
+	result += numberPart
+
+	return result
+}
+
 // fillCellData attempts to extract a valid value, usable in
 // CSV form from the raw cell value.  Note - this is not actually
 // general enough - we should support retaining tabs and newlines.

+ 53 - 0
lib_test.go

@@ -1120,6 +1120,59 @@ func (l *LibSuite) TestSharedFormulas(c *C) {
 	c.Assert(row.Cells[2].Formula(), Equals, "2*C1")
 }
 
+// Test shared formulas that have absolute references ($) in them
+func (l *LibSuite) TestSharedFormulasWithAbsoluteReferences(c *C) {
+	formulas := []string{
+		"A1",
+		"$A1",
+		"A$1",
+		"$A$1",
+		"A1+B1",
+		"$A1+B1",
+		"$A$1+B1",
+		"A1+$B1",
+		"A1+B$1",
+		"A1+$B$1",
+		"$A$1+$B$1",
+	}
+
+	expected := []string{
+		"B2",
+		"$A2",
+		"B$1",
+		"$A$1",
+		"B2+C2",
+		"$A2+C2",
+		"$A$1+C2",
+		"B2+$B2",
+		"B2+C$1",
+		"B2+$B$1",
+		"$A$1+$B$1",
+	}
+
+	anchorCell := "C4"
+
+	sharedFormulas := map[int]sharedFormula{}
+	x, y, _ := getCoordsFromCellIDString(anchorCell)
+	for i, formula := range formulas {
+		res := formula
+		sharedFormulas[i] = sharedFormula{x, y, res}
+	}
+
+	for i, formula := range formulas {
+		testCell := xlsxC{
+			R: "D5",
+			F: &xlsxF{
+				Content: formula,
+				T:       "shared",
+				Si:      i,
+			},
+		}
+
+		c.Assert(formulaForCell(testCell, sharedFormulas), Equals, expected[i])
+	}
+}
+
 // Avoid panic when cell.F.T is "e" (for error)
 func (l *LibSuite) TestFormulaForCellPanic(c *C) {
 	cell := xlsxC{R: "A1"}