Browse Source

1 node and 2 node test cases pass

Geoffrey J. Teale 6 years ago
parent
commit
66648b2a40
2 changed files with 248 additions and 31 deletions
  1. 99 18
      col.go
  2. 149 13
      col_test.go

+ 99 - 18
col.go

@@ -120,32 +120,112 @@ func (c *Col) GetStreamStyle() StreamStyle {
 	return StreamStyle{builtInNumFmtInv[c.numFmt], c.style}
 	return StreamStyle{builtInNumFmtInv[c.numFmt], c.style}
 }
 }
 
 
+// copyToRange is an internal convenience function to make a copy of a
+// Col with a different Min and Max value, it is not intended as a
+// general purpose Col copying function as you must still insert the
+// resulting Col into the ColStore.
+func (c *Col) copyToRange(min, max int) *Col {
+	return &Col{
+		Min:             min,
+		Max:             max,
+		Hidden:          c.Hidden,
+		Width:           c.Width,
+		Collapsed:       c.Collapsed,
+		OutlineLevel:    c.OutlineLevel,
+		numFmt:          c.numFmt,
+		parsedNumFmt:    c.parsedNumFmt,
+		style:           c.style,
+		DataValidation:  append([]*xlsxCellDataValidation{}, c.DataValidation...),
+		defaultCellType: c.defaultCellType,
+	}
+}
+
 type colStoreNode struct {
 type colStoreNode struct {
 	Col  *Col
 	Col  *Col
 	Prev *colStoreNode
 	Prev *colStoreNode
 	Next *colStoreNode
 	Next *colStoreNode
 }
 }
 
 
-func (csn *colStoreNode) placeNode(node *colStoreNode) error {
+// makeWay will adjust the Min and Max of this colStoreNode's Col to
+// make way for a new colStoreNode's Col. If necessary it will
+// generate an additional colStoreNode with a new Col covering the
+// "tail" portion of this colStoreNode's Col should the new node lay
+// completely within the range of this one, but without reaching its
+// maximum extent.
+func (csn *colStoreNode) makeWay(node *colStoreNode) {
 	switch {
 	switch {
 	case csn.Col.Max < node.Col.Min:
 	case csn.Col.Max < node.Col.Min:
-		if csn.Next == nil {
-			csn.Next = node
-			return nil
+		// The new node starts after this one ends, there's no overlap
+		//
+		// Node1 |----|
+		// Node2        |----|
+		if csn.Next != nil {
+			csn.Next.makeWay(node)
+			return
 		}
 		}
-	}
-
-	if node.Col.Min <= csn.Col.Min {
-		if csn.Prev == nil {
-			csn.Prev = node
-		} else {
-			err := csn.Prev.placeNode(node)
-			if err != nil {
-				return err
-			}
+		csn.Next = node
+		node.Prev = csn
+		return
+
+	case csn.Col.Min > node.Col.Max:
+		// The new node ends before this one begins, there's no overlap
+		//
+		// Node1         |-----|
+		// Node2  |----|
+		if csn.Prev != nil {
+			csn.Prev.makeWay(node)
+			return
+		}
+		csn.Prev = node
+		node.Next = csn
+		return
+
+	case csn.Col.Min < node.Col.Min && csn.Col.Max > node.Col.Max:
+		// The new node bisects this one:
+		//
+		// Node1 |---xx---|
+		// Node2    |--|
+		newCol := csn.Col.copyToRange(node.Col.Max+1, csn.Col.Max)
+		newNode := &colStoreNode{Col: newCol, Prev: node, Next: csn.Next}
+		csn.Col.Max = node.Col.Min - 1
+		csn.Next = node
+		node.Prev = csn
+		node.Next = newNode
+		return
+
+	case csn.Col.Max >= node.Col.Min && csn.Col.Min < node.Col.Min:
+		// The new node overlaps this one at some point above it's minimum:
+		//
+		//  Node1  |----xx|
+		//  Node2      |-------|
+		csn.Col.Max = node.Col.Min - 1
+		if csn.Next != nil {
+			// Break the link to this node, which prevents
+			// us looping back and forth forever
+			csn.Next.Prev = nil
+			csn.Next.makeWay(node)
+		}
+		csn.Next = node
+		node.Prev = csn
+		return
+
+	case csn.Col.Min <= node.Col.Max && csn.Col.Min > node.Col.Min:
+		// The new node overlaps this one at some point below it's maximum:
+		//
+		// Node1:     |------|
+		// Node2: |----xx|
+		csn.Col.Min = node.Col.Max + 1
+		if csn.Prev != nil {
+			// Break the link to this node, which prevents
+			// us looping back and forth forever
+			csn.Prev.Next = nil
+			csn.Prev.makeWay(node)
 		}
 		}
+		csn.Prev = node
+		node.Next = csn
+		return
 	}
 	}
-	return nil
+	return
 }
 }
 
 
 type ColStore struct {
 type ColStore struct {
@@ -153,11 +233,12 @@ type ColStore struct {
 }
 }
 
 
 //
 //
-func (cs *ColStore) Add(col *Col) error {
+func (cs *ColStore) Add(col *Col) {
 	newNode := &colStoreNode{Col: col}
 	newNode := &colStoreNode{Col: col}
 	if cs.Root == nil {
 	if cs.Root == nil {
 		cs.Root = newNode
 		cs.Root = newNode
-		return nil
+		return
 	}
 	}
-	return cs.Root.placeNode(newNode)
+	cs.Root.makeWay(newNode)
+	return
 }
 }

+ 149 - 13
col_test.go

@@ -4,26 +4,162 @@ import (
 	. "gopkg.in/check.v1"
 	. "gopkg.in/check.v1"
 )
 )
 
 
+type ColSuite struct{}
+
+var _ = Suite(&ColSuite{})
+
+func (cs *ColSuite) TestCopyToRange(c *C) {
+	nf := &parsedNumberFormat{}
+	s := &Style{}
+	cdv1 := &xlsxCellDataValidation{}
+	cdv2 := &xlsxCellDataValidation{}
+	ct := CellTypeBool.Ptr()
+	c1 := &Col{
+		Min:             0,
+		Max:             10,
+		Hidden:          true,
+		Width:           300.4,
+		Collapsed:       true,
+		OutlineLevel:    2,
+		numFmt:          "-0.00",
+		parsedNumFmt:    nf,
+		style:           s,
+		DataValidation:  []*xlsxCellDataValidation{cdv1, cdv2},
+		defaultCellType: ct,
+	}
+
+	c2 := c1.copyToRange(4, 10)
+	c.Assert(c2.Min, Equals, 4)
+	c.Assert(c2.Max, Equals, 10)
+	c.Assert(c2.Hidden, Equals, c1.Hidden)
+	c.Assert(c2.Width, Equals, c1.Width)
+	c.Assert(c2.Collapsed, Equals, c1.Collapsed)
+	c.Assert(c2.OutlineLevel, Equals, c1.OutlineLevel)
+	c.Assert(c2.numFmt, Equals, c1.numFmt)
+	c.Assert(c2.parsedNumFmt, Equals, c1.parsedNumFmt)
+	c.Assert(c2.style, Equals, c1.style)
+	c.Assert(c2.DataValidation, HasLen, 2)
+	c.Assert(c2.DataValidation[0], Equals, c1.DataValidation[0])
+	c.Assert(c2.DataValidation[1], Equals, c1.DataValidation[1])
+	c.Assert(c2.defaultCellType, Equals, c1.defaultCellType)
+}
+
 type ColStoreSuite struct{}
 type ColStoreSuite struct{}
 
 
 var _ = Suite(&ColStoreSuite{})
 var _ = Suite(&ColStoreSuite{})
 
 
-func (css *ColStoreSuite) TestAddOneNode(c *C) {
+func (css *ColStoreSuite) TestAddRootNode(c *C) {
 	col := &Col{Min: 0, Max: 1}
 	col := &Col{Min: 0, Max: 1}
 	cs := ColStore{}
 	cs := ColStore{}
-	err := cs.Add(col)
-	c.Assert(err, IsNil)
+	cs.Add(col)
 	c.Assert(cs.Root.Col, Equals, col)
 	c.Assert(cs.Root.Col, Equals, col)
 }
 }
 
 
-func (css *ColStoreSuite) TestAddTwoNonOverlappingSequentialNodes(c *C) {
-	col1 := &Col{Min: 0, Max: 1}
-	col2 := &Col{Min: 2, Max: 4}
-	cs := ColStore{}
-	err := cs.Add(col1)
-	c.Assert(err, IsNil)
-	err = cs.Add(col2)
-	c.Assert(err, IsNil)
-	c.Assert(cs.Root.Col, Equals, col1)
-	c.Assert(cs.Root.Next.Col, Equals, col2)
+func (css *ColStoreSuite) TestMakeWay(c *C) {
+	assertWayMade := func(cols []*Col, chainFunc func(root *colStoreNode)) {
+
+		cs := ColStore{}
+		for _, col := range cols {
+			cs.Add(col)
+		}
+		chainFunc(cs.Root)
+	}
+
+	// Col1: |--|
+	// Col2:    |--|
+	assertWayMade([]*Col{&Col{Min: 0, Max: 1}, &Col{Min: 2, Max: 3}},
+		func(root *colStoreNode) {
+			c.Assert(root.Col.Min, Equals, 0)
+			c.Assert(root.Col.Max, Equals, 1)
+			c.Assert(root.Prev, IsNil)
+			c.Assert(root.Next, NotNil)
+			node2 := root.Next
+			c.Assert(node2.Prev, Equals, root)
+			c.Assert(node2.Next, IsNil)
+			c.Assert(node2.Col.Min, Equals, 2)
+			c.Assert(node2.Col.Max, Equals, 3)
+		})
+
+	// Col1:    |--|
+	// Col2: |--|
+	assertWayMade([]*Col{&Col{Min: 2, Max: 3}, &Col{Min: 0, Max: 1}},
+		func(root *colStoreNode) {
+			c.Assert(root.Col.Min, Equals, 2)
+			c.Assert(root.Col.Max, Equals, 3)
+			c.Assert(root.Prev, NotNil)
+			c.Assert(root.Next, IsNil)
+			node2 := root.Prev
+			c.Assert(node2.Next, Equals, root)
+			c.Assert(node2.Prev, IsNil)
+			c.Assert(node2.Col.Min, Equals, 0)
+			c.Assert(node2.Col.Max, Equals, 1)
+		})
+
+	// Col1: |--x|
+	// Col2:   |--|
+	assertWayMade([]*Col{&Col{Min: 0, Max: 2}, &Col{Min: 2, Max: 3}},
+		func(root *colStoreNode) {
+			c.Assert(root.Col.Min, Equals, 0)
+			c.Assert(root.Col.Max, Equals, 1)
+			c.Assert(root.Prev, IsNil)
+			c.Assert(root.Next, NotNil)
+			node2 := root.Next
+			c.Assert(node2.Prev, Equals, root)
+			c.Assert(node2.Next, IsNil)
+			c.Assert(node2.Col.Min, Equals, 2)
+			c.Assert(node2.Col.Max, Equals, 3)
+		})
+
+	// Col1:  |x-|
+	// Col2: |--|
+	assertWayMade([]*Col{&Col{Min: 1, Max: 2}, &Col{Min: 0, Max: 1}},
+		func(root *colStoreNode) {
+			c.Assert(root.Col.Min, Equals, 2)
+			c.Assert(root.Col.Max, Equals, 2)
+			c.Assert(root.Prev, NotNil)
+			c.Assert(root.Next, IsNil)
+			node2 := root.Prev
+			c.Assert(node2.Next, Equals, root)
+			c.Assert(node2.Prev, IsNil)
+			c.Assert(node2.Col.Min, Equals, 0)
+			c.Assert(node2.Col.Max, Equals, 1)
+		})
+
+	// Col1: |---xx---|
+	// Col2:    |--|
+	assertWayMade([]*Col{&Col{Min: 0, Max: 7}, &Col{Min: 3, Max: 4}},
+		func(root *colStoreNode) {
+			c.Assert(root.Prev, IsNil)
+			c.Assert(root.Next, NotNil)
+			node2 := root.Next
+			c.Assert(node2.Prev, Equals, root)
+			c.Assert(node2.Col.Min, Equals, 3)
+			c.Assert(node2.Col.Max, Equals, 4)
+			c.Assert(node2.Next, NotNil)
+			node3 := node2.Next
+			c.Assert(node3.Prev, Equals, node2)
+			c.Assert(node3.Next, IsNil)
+			c.Assert(node3.Col.Min, Equals, 5)
+			c.Assert(node3.Col.Max, Equals, 7)
+		})
+
+	// Col1: |--|
+	// Col2:          |--|
+	// Col3:     |
+	assertWayMade([]*Col{&Col{Min: 0, Max: 7}, &Col{Min: 3, Max: 4}},
+		func(root *colStoreNode) {
+			c.Assert(root.Prev, IsNil)
+			c.Assert(root.Next, NotNil)
+			node2 := root.Next
+			c.Assert(node2.Prev, Equals, root)
+			c.Assert(node2.Col.Min, Equals, 3)
+			c.Assert(node2.Col.Max, Equals, 4)
+			c.Assert(node2.Next, NotNil)
+			node3 := node2.Next
+			c.Assert(node3.Prev, Equals, node2)
+			c.Assert(node3.Next, IsNil)
+			c.Assert(node3.Col.Min, Equals, 5)
+			c.Assert(node3.Col.Max, Equals, 7)
+		})
+
 }
 }