Add SetData method to CharData.

This makes it possible for CharData.IsWhitespace to return true
when the whitespace was set by the user, not just when it was
deserialized or produced by an indent method.
diff --git a/etree.go b/etree.go
index fcfd6f2..a24d4d4 100644
--- a/etree.go
+++ b/etree.go
@@ -58,7 +58,6 @@
 			entityCopy[k] = v
 		}
 	}
-
 	return ReadSettings{
 		CharsetReader: s.CharsetReader,
 		Permissive:    s.Permissive,
@@ -145,7 +144,7 @@
 type charDataFlags uint8
 
 const (
-	// The CharData was created by an indent function as whitespace.
+	// The CharData contains only whitespace.
 	whitespaceFlag charDataFlags = 1 << iota
 
 	// The CharData contains a CDATA section.
@@ -153,7 +152,8 @@
 )
 
 // CharData can be used to represent character data or a CDATA section within
-// an XML document.
+// an XML document. The character's Data property should not be modified
+// directly; use the SetData method instead.
 type CharData struct {
 	Data   string
 	parent *Element
@@ -1220,13 +1220,13 @@
 	return newCharData(data, 0, e)
 }
 
-// dup duplicates the character data.
-func (c *CharData) dup(parent *Element) Token {
-	return &CharData{
-		Data:   c.Data,
-		flags:  c.flags,
-		parent: parent,
-		index:  c.index,
+// SetData modifies the text content of the CharData token.
+func (c *CharData) SetData(text string) {
+	c.Data = text
+	if isWhitespace(text) {
+		c.flags |= whitespaceFlag
+	} else {
+		c.flags &= ^whitespaceFlag
 	}
 }
 
@@ -1236,8 +1236,8 @@
 	return (c.flags & cdataFlag) != 0
 }
 
-// IsWhitespace returns true if the character data token was created by one of
-// the document Indent methods to contain only whitespace.
+// IsWhitespace returns true if the character data token contains only
+// whitespace.
 func (c *CharData) IsWhitespace() bool {
 	return (c.flags & whitespaceFlag) != 0
 }
@@ -1255,6 +1255,16 @@
 	return c.index
 }
 
+// dup duplicates the character data.
+func (c *CharData) dup(parent *Element) Token {
+	return &CharData{
+		Data:   c.Data,
+		flags:  c.flags,
+		parent: parent,
+		index:  c.index,
+	}
+}
+
 // setParent replaces the character data token's parent.
 func (c *CharData) setParent(parent *Element) {
 	c.parent = parent
diff --git a/etree_test.go b/etree_test.go
index bf2f6f5..afe6aee 100644
--- a/etree_test.go
+++ b/etree_test.go
@@ -11,6 +11,16 @@
 	"testing"
 )
 
+func newDocumentFromString(t *testing.T, s string) *Document {
+	t.Helper()
+	doc := NewDocument()
+	err := doc.ReadFromString(s)
+	if err != nil {
+		t.Error("etree: failed to parse document")
+	}
+	return doc
+}
+
 func checkStrEq(t *testing.T, got, want string) {
 	t.Helper()
 	if got != want {
@@ -32,6 +42,13 @@
 	}
 }
 
+func checkBoolEq(t *testing.T, got, want bool) {
+	t.Helper()
+	if got != want {
+		t.Errorf("etree: unexpected boolean. Got: %v. Wanted: %v\n", got, want)
+	}
+}
+
 func checkElementEq(t *testing.T, got, want *Element) {
 	t.Helper()
 	if got != want {
@@ -1031,11 +1048,7 @@
 	</child3>
 </a:root>`
 
-	doc := NewDocument()
-	err := doc.ReadFromString(s)
-	if err != nil {
-		t.Error("etree: failed to parse document")
-	}
+	doc := newDocumentFromString(t, s)
 
 	root := doc.SelectElement("root")
 	child1 := root.SelectElement("child1")
@@ -1083,3 +1096,51 @@
 		t.Error("etree: failed namespace-uri test")
 	}
 }
+
+func TestWhitespace(t *testing.T) {
+	s := "<root>\n\t<child>\n\t\t<grandchild> x</grandchild>\n    </child>\n</root>"
+
+	doc := newDocumentFromString(t, s)
+	root := doc.Root()
+	checkIntEq(t, len(root.Child), 3)
+
+	cd := root.Child[0].(*CharData)
+	checkBoolEq(t, cd.IsWhitespace(), true)
+	checkStrBinaryEq(t, cd.Data, "\n\t")
+
+	cd = root.Child[2].(*CharData)
+	checkBoolEq(t, cd.IsWhitespace(), true)
+	checkStrBinaryEq(t, cd.Data, "\n")
+
+	child := root.SelectElement("child")
+	checkIntEq(t, len(child.Child), 3)
+
+	cd = child.Child[0].(*CharData)
+	checkBoolEq(t, cd.IsWhitespace(), true)
+	checkStrBinaryEq(t, cd.Data, "\n\t\t")
+
+	cd = child.Child[2].(*CharData)
+	checkBoolEq(t, cd.IsWhitespace(), true)
+	checkStrBinaryEq(t, cd.Data, "\n    ")
+
+	grandchild := child.SelectElement("grandchild")
+	checkIntEq(t, len(grandchild.Child), 1)
+
+	cd = grandchild.Child[0].(*CharData)
+	checkBoolEq(t, cd.IsWhitespace(), false)
+
+	cd.SetData(" ")
+	checkBoolEq(t, cd.IsWhitespace(), true)
+
+	cd.SetData("        x")
+	checkBoolEq(t, cd.IsWhitespace(), false)
+
+	cd.SetData("\t\n\r    ")
+	checkBoolEq(t, cd.IsWhitespace(), true)
+
+	cd.SetData("\uFFFD")
+	checkBoolEq(t, cd.IsWhitespace(), false)
+
+	cd.SetData("")
+	checkBoolEq(t, cd.IsWhitespace(), true)
+}