فهرست منبع

Working implementation of numericToLetters.

Geoffrey J. Teale 11 سال پیش
والد
کامیت
04306188fc
6فایلهای تغییر یافته به همراه179 افزوده شده و 6 حذف شده
  1. 11 0
      cell_test.go
  2. 60 0
      lib.go
  3. 51 1
      lib_test.go
  4. 21 0
      sheet.go
  5. 26 0
      sheet_test.go
  6. 10 5
      worksheet.go

+ 11 - 0
cell_test.go

@@ -8,6 +8,17 @@ type CellSuite struct {}
 
 
 var _ = Suite(&CellSuite{})
 var _ = Suite(&CellSuite{})
 
 
+// Test that we can set and get a Value from a Cell
+func (s *CellSuite) TestValueSet(c *C) {
+	// Note, this test is fairly pointless, it serves mostly to
+	// reinforce that this functionality is important, and should
+	// the mechanics of this all change at some point, to remind
+	// us not to lose this.
+	cell := Cell{}
+	cell.Value = "A string"
+	c.Assert(cell.Value, Equals, "A string")
+}
+
 // Test that GetStyle correctly converts the xlsxStyle.Fonts.
 // Test that GetStyle correctly converts the xlsxStyle.Fonts.
 func (s *CellSuite) TestGetStyleWithFonts(c *C) {
 func (s *CellSuite) TestGetStyleWithFonts(c *C) {
 	var cell *Cell
 	var cell *Cell

+ 60 - 0
lib.go

@@ -63,6 +63,58 @@ func lettersToNumeric(letters string) int {
 	return sum
 	return sum
 }
 }
 
 
+
+// Get the largestDenominator that is a multiple of a basedDenominator
+// and fits at least once into a given numerator.
+func getLargestDenominator(numerator, multiple, baseDenominator, power int) (int, int) {
+	if numerator / multiple == 0 {
+		return 1, power
+	}
+	next, nextPower := getLargestDenominator(
+		numerator, multiple * baseDenominator, baseDenominator, power + 1)
+	if next > multiple {
+		return next, nextPower
+	}
+	return multiple, power
+}
+
+// numericToLetters is used to convert a zero based, numeric column
+// indentifier into a character code.
+func numericToLetters(colRef int) string {
+	b26Denominator, _ := getLargestDenominator(colRef, 1, 26, 0)
+	parts := []int{}
+	x := colRef
+	for d := b26Denominator; d > 0; d = d / 26 {
+		value := x / d
+		remainder := x % d
+		parts = append(parts, value)
+		x = remainder
+	}
+	lastPart := len(parts) - 1
+	for i := lastPart -1; i > 0; i-- {
+		part := parts[i]
+		if part == 0 {
+			greaterPart := parts[i-1]
+			if greaterPart > 0 {
+				parts[i-1] = greaterPart - 1
+				parts[i] = 26
+			}
+		}
+	}
+	result := ""
+	for n, part := range(parts) {
+		fmt.Printf("c = %d, n = %d, p = %d, l = %d\n", colRef, n, part, lastPart)
+		if n == lastPart {
+			result += string(part + 65)
+		} else {
+			if part > 0 {
+				result += string(part + 64)
+			}
+		}
+	}
+	return result
+}
+
 // letterOnlyMapF is used in conjunction with strings.Map to return
 // letterOnlyMapF is used in conjunction with strings.Map to return
 // only the characters A-Z and a-z in a string
 // only the characters A-Z and a-z in a string
 func letterOnlyMapF(rune rune) rune {
 func letterOnlyMapF(rune rune) rune {
@@ -98,6 +150,14 @@ func getCoordsFromCellIDString(cellIDString string) (x, y int, error error) {
 	return x, y, error
 	return x, y, error
 }
 }
 
 
+// getCellIDStringFromCoords returns the Excel format cell name that
+// represents a pair of zero based cartesian coordinates.
+func getCellIDStringFromCoords(x, y int) string {
+	letterPart := numericToLetters(x);
+	numericPart := y + 1
+	return fmt.Sprintf("%s%d", letterPart, numericPart)
+}
+
 // getMaxMinFromDimensionRef return the zero based cartesian maximum
 // getMaxMinFromDimensionRef return the zero based cartesian maximum
 // and minimum coordinates from the dimension reference embedded in a
 // and minimum coordinates from the dimension reference embedded in a
 // XLSX worksheet.  For example, the dimension reference "A1:B2"
 // XLSX worksheet.  For example, the dimension reference "A1:B2"

+ 51 - 1
lib_test.go

@@ -137,9 +137,36 @@ func (l *LibSuite) TestReadWorkbookRelationsFromZipFileWithFunnyNames(c *C) {
 	c.Assert(cell1.String(), Equals, "I am Bob")
 	c.Assert(cell1.String(), Equals, "I am Bob")
 }
 }
 
 
+func (l *LibSuite) TestGetLargestDenominator(c *C) {
+	d, p := getLargestDenominator(0, 1, 2, 0)
+	c.Assert(d, Equals, 1)
+	c.Assert(p, Equals, 0)
+	d, p = getLargestDenominator(1, 1, 2, 0)
+	c.Assert(d, Equals, 1)
+	c.Assert(p, Equals, 0)
+	d, p = getLargestDenominator(2, 1, 2, 0)
+	c.Assert(d, Equals, 2)
+	c.Assert(p, Equals, 1)
+	d, p = getLargestDenominator(4, 1, 2, 0)
+	c.Assert(d, Equals, 4)
+	c.Assert(p, Equals, 2)
+	d, p = getLargestDenominator(8, 1, 2, 0)
+	c.Assert(d, Equals, 8)
+	c.Assert(p, Equals, 3)
+	d, p = getLargestDenominator(9, 1, 2, 0)
+	c.Assert(d, Equals, 8)
+	c.Assert(p, Equals, 3)
+	d, p = getLargestDenominator(15,1, 2, 0)
+	c.Assert(d, Equals, 8)
+	c.Assert(p, Equals, 3)
+	d, p = getLargestDenominator(16,1, 2, 0)
+	c.Assert(d, Equals, 16)
+	c.Assert(p, Equals, 4)
+}
+
 func (l *LibSuite) TestLettersToNumeric(c *C) {
 func (l *LibSuite) TestLettersToNumeric(c *C) {
 	cases := map[string]int{"A": 0, "G": 6, "z": 25, "AA": 26, "Az": 51,
 	cases := map[string]int{"A": 0, "G": 6, "z": 25, "AA": 26, "Az": 51,
-		"BA": 52, "Bz": 77, "ZA": 26*26 + 0, "ZZ": 26*26 + 25,
+		"BA": 52, "BZ": 77, "ZA": 26*26 + 0, "ZZ": 26*26 + 25,
 		"AAA": 26*26 + 26 + 0, "AMI": 1022}
 		"AAA": 26*26 + 26 + 0, "AMI": 1022}
 	for input, ans := range cases {
 	for input, ans := range cases {
 		output := lettersToNumeric(input)
 		output := lettersToNumeric(input)
@@ -147,6 +174,24 @@ func (l *LibSuite) TestLettersToNumeric(c *C) {
 	}
 	}
 }
 }
 
 
+func (l *LibSuite) TestNumericToLetters(c *C) {
+	cases := map[string]int{
+		"A": 0,
+		"G": 6,
+		"Z": 25,
+		"AA": 26,
+		"AZ": 51,
+		"BA": 52,
+		"BZ": 77, "ZA": 26*26, "ZB": 26*26 + 1,
+		"ZZ": 26*26 + 25,
+		"AAA": 26*26 + 26 + 0, "AMI": 1022}
+	for ans, input := range cases {
+		output := numericToLetters(input)
+		c.Assert(output, Equals, ans)
+	}
+
+}
+
 func (l *LibSuite) TestLetterOnlyMapFunction(c *C) {
 func (l *LibSuite) TestLetterOnlyMapFunction(c *C) {
 	var input string = "ABC123"
 	var input string = "ABC123"
 	var output string = strings.Map(letterOnlyMapF, input)
 	var output string = strings.Map(letterOnlyMapF, input)
@@ -172,6 +217,11 @@ func (l *LibSuite) TestGetCoordsFromCellIDString(c *C) {
 	c.Assert(y, Equals, 2)
 	c.Assert(y, Equals, 2)
 }
 }
 
 
+func (l *LibSuite) TestGetCellIDStringFromCoords(c *C){
+	c.Assert(getCellIDStringFromCoords(0, 0), Equals, "A1")
+	c.Assert(getCellIDStringFromCoords(2, 2), Equals, "C3")
+}
+
 func (l *LibSuite) TestGetMaxMinFromDimensionRef(c *C) {
 func (l *LibSuite) TestGetMaxMinFromDimensionRef(c *C) {
 	var dimensionRef string = "A1:B2"
 	var dimensionRef string = "A1:B2"
 	var minx, miny, maxx, maxy int
 	var minx, miny, maxx, maxy int

+ 21 - 0
sheet.go

@@ -1,5 +1,9 @@
 package xlsx
 package xlsx
 
 
+import (
+	"encoding/xml"
+)
+
 // Sheet is a high level structure intended to provide user access to
 // Sheet is a high level structure intended to provide user access to
 // the contents of a particular sheet within an XLSX file.
 // the contents of a particular sheet within an XLSX file.
 type Sheet struct {
 type Sheet struct {
@@ -17,3 +21,20 @@ func (s *Sheet) AddRow() *Row {
 	}
 	}
 	return row
 	return row
 }
 }
+
+// Dump sheet to it's XML representation
+func (s *Sheet) makeXLSXSheet() ([]byte, error) {
+	xSheet := xlsxSheetData{}
+	for r, row := range s.Rows {
+		xRow := xlsxRow{}
+		xRow.R = r + 1
+		for _, cell := range row.Cells {
+			xC := xlsxC{}
+			xC.V = cell.Value
+			xC.T = "s" // Hardcode string type, for now.
+			xRow.C = append(xRow.C, xC)
+		}
+		xSheet.Row = append(xSheet.Row, xRow)
+	}
+	return xml.MarshalIndent(xSheet, "  ", "  ")
+}

+ 26 - 0
sheet_test.go

@@ -17,3 +17,29 @@ func (s *SheetSuite) TestAddRow(c *C) {
 	c.Assert(row, NotNil)
 	c.Assert(row, NotNil)
 	c.Assert(len(sheet.Rows), Equals, 1)
 	c.Assert(len(sheet.Rows), Equals, 1)
 }
 }
+
+func (s *SheetSuite) TestMakeXLSXSheetFromRows(c *C) {
+	file := NewFile()
+	sheet := file.AddSheet("Sheet1")
+	row := sheet.AddRow()
+	cell := row.AddCell()
+	cell.Value = "A cell!"
+	xSheet, err := sheet.makeXLSXSheet()
+	c.Assert(err, IsNil)
+	expectedXLSXSheet := `
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"
+           xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+  <dimension ref="A1:A1"/>
+  <sheetData>
+    <row r="1">
+      <c r="A1" t="s">
+        <v>A cell!</v>
+      </c>
+    </row>
+  </sheetData>
+</worksheet>
+`
+	c.Assert(string(xSheet), Equals, expectedXLSXSheet)
+}
+

+ 10 - 5
worksheet.go

@@ -1,5 +1,9 @@
 package xlsx
 package xlsx
 
 
+import (
+	"encoding/xml"
+)
+
 // xlsxWorksheet directly maps the worksheet element in the namespace
 // xlsxWorksheet directly maps the worksheet element in the namespace
 // http://schemas.openxmlformats.org/spreadsheetml/2006/main -
 // http://schemas.openxmlformats.org/spreadsheetml/2006/main -
 // currently I have not checked it for completeness - it does as much
 // currently I have not checked it for completeness - it does as much
@@ -22,6 +26,7 @@ type xlsxDimension struct {
 // currently I have not checked it for completeness - it does as much
 // currently I have not checked it for completeness - it does as much
 // as I need.
 // as I need.
 type xlsxSheetData struct {
 type xlsxSheetData struct {
+	XMLName xml.Name `xml:"sheetData"`
 	Row []xlsxRow `xml:"row"`
 	Row []xlsxRow `xml:"row"`
 }
 }
 
 
@@ -36,14 +41,14 @@ type xlsxRow struct {
 }
 }
 
 
 // xlsxC directly maps the c element in the namespace
 // xlsxC directly maps the c element in the namespace
-// http://schemas.openxmlformats.org/spreadsheetml/2006/main -
+// http://schemas.openxmlformats.org/sprceadsheetml/2006/main -
 // currently I have not checked it for completeness - it does as much
 // currently I have not checked it for completeness - it does as much
 // as I need.
 // as I need.
 type xlsxC struct {
 type xlsxC struct {
-	R string `xml:"r,attr"`
-	S int    `xml:"s,attr"`
-	T string `xml:"t,attr"`
-	V string `xml:"v"`
+	R string `xml:"r,attr"`  // Cell ID, e.g. A1
+	S int    `xml:"s,attr"`  // Style reference.
+	T string `xml:"t,attr"`  // Type.
+	V string `xml:"v"`       // Value
 }
 }
 
 
 // get cell
 // get cell