.
ElementNode
// AttributeNode is an attribute, such as id='123'.
AttributeNode
// TextNode is the text content of a node.
TextNode
// CommentNode is a comment node, such as
CommentNode
// allNode is any types of node, used by xpath package only to predicate match.
allNode
)
// NodeNavigator provides cursor model for navigating XML data.
type NodeNavigator interface {
// NodeType returns the XPathNodeType of the current node.
NodeType() NodeType
// LocalName gets the Name of the current node.
LocalName() string
// Prefix returns namespace prefix associated with the current node.
Prefix() string
// Value gets the value of current node.
Value() string
// Copy does a deep copy of the NodeNavigator and all its components.
Copy() NodeNavigator
// MoveToRoot moves the NodeNavigator to the root node of the current node.
MoveToRoot()
// MoveToParent moves the NodeNavigator to the parent node of the current node.
MoveToParent() bool
// MoveToNextAttribute moves the NodeNavigator to the next attribute on current node.
MoveToNextAttribute() bool
// MoveToChild moves the NodeNavigator to the first child node of the current node.
MoveToChild() bool
// MoveToFirst moves the NodeNavigator to the first sibling node of the current node.
MoveToFirst() bool
// MoveToNext moves the NodeNavigator to the next sibling node of the current node.
MoveToNext() bool
// MoveToPrevious moves the NodeNavigator to the previous sibling node of the current node.
MoveToPrevious() bool
// MoveTo moves the NodeNavigator to the same position as the specified NodeNavigator.
MoveTo(NodeNavigator) bool
}
// NodeIterator holds all matched Node object.
type NodeIterator struct {
node NodeNavigator
query query
}
// Current returns current node which matched.
func (t *NodeIterator) Current() NodeNavigator {
return t.node
}
// MoveNext moves Navigator to the next match node.
func (t *NodeIterator) MoveNext() bool {
n := t.query.Select(t)
if n == nil {
return false
}
if !t.node.MoveTo(n) {
t.node = n.Copy()
}
return true
}
// Select selects a node set using the specified XPath expression.
// This method is deprecated, recommend using Expr.Select() method instead.
func Select(root NodeNavigator, expr string) *NodeIterator {
exp, err := Compile(expr)
if err != nil {
panic(err)
}
return exp.Select(root)
}
// Expr is an XPath expression for query.
type Expr struct {
s string
q query
}
type iteratorFunc func() NodeNavigator
func (f iteratorFunc) Current() NodeNavigator {
return f()
}
// Evaluate returns the result of the expression.
// The result type of the expression is one of the follow: bool,float64,string,NodeIterator).
func (expr *Expr) Evaluate(root NodeNavigator) interface{} {
val := expr.q.Evaluate(iteratorFunc(func() NodeNavigator { return root }))
switch val.(type) {
case query:
return &NodeIterator{query: expr.q.Clone(), node: root}
}
return val
}
// Select selects a node set using the specified XPath expression.
func (expr *Expr) Select(root NodeNavigator) *NodeIterator {
return &NodeIterator{query: expr.q.Clone(), node: root}
}
// String returns XPath expression string.
func (expr *Expr) String() string {
return expr.s
}
// Compile compiles an XPath expression string.
func Compile(expr string) (*Expr, error) {
if expr == "" {
return nil, errors.New("expr expression is nil")
}
qy, err := build(expr, nil)
if err != nil {
return nil, err
}
if qy == nil {
return nil, fmt.Errorf(fmt.Sprintf("undeclared variable in XPath expression: %s", expr))
}
return &Expr{s: expr, q: qy}, nil
}
// MustCompile compiles an XPath expression string and ignored error.
func MustCompile(expr string) *Expr {
exp, err := Compile(expr)
if err != nil {
return &Expr{s: expr, q: nopQuery{}}
}
return exp
}
// CompileWithNS compiles an XPath expression string, using given namespaces map.
func CompileWithNS(expr string, namespaces map[string]string) (*Expr, error) {
if expr == "" {
return nil, errors.New("expr expression is nil")
}
qy, err := build(expr, namespaces)
if err != nil {
return nil, err
}
if qy == nil {
return nil, fmt.Errorf(fmt.Sprintf("undeclared variable in XPath expression: %s", expr))
}
return &Expr{s: expr, q: qy}, nil
}
golang-github-antchfx-xpath-1.3.5/xpath_axes_test.go 0000664 0000000 0000000 00000010002 15052615265 0022554 0 ustar 00root root 0000000 0000000 package xpath
import "testing"
func Test_self(t *testing.T) {
test_xpath_elements(t, employee_example, `//name/self::*`, 4, 9, 14)
}
func Test_child(t *testing.T) {
test_xpath_elements(t, employee_example, `//child::employee/child::email`, 6, 11, 16)
test_xpath_elements(t, employee_example, `/empinfo/child::*`, 3, 8, 13)
test_xpath_elements(t, employee_example, `/empinfo/child::node()`, 3, 8, 13)
test_xpath_values(t, employee_example, `//name/child::text()`, "Opal Kole", "Max Miller", "Beccaa Moss")
}
func Test_descendant(t *testing.T) {
test_xpath_elements(t, employee_example, `//employee/descendant::*`, 4, 5, 6, 9, 10, 11, 14, 15, 16)
test_xpath_count(t, employee_example, `//descendant::employee`, 3)
}
func Test_descendant_or_self(t *testing.T) {
test_xpath_tags(t, employee_example.FirstChild, `self::*`, "empinfo")
test_xpath_elements(t, employee_example, `//employee/descendant-or-self::*`, 3, 4, 5, 6, 8, 9, 10, 11, 13, 14, 15, 16)
test_xpath_count(t, employee_example, `//descendant-or-self::employee`, 3)
}
func Test_ancestor(t *testing.T) {
test_xpath_tags(t, employee_example, `//employee/ancestor::*`, "empinfo")
test_xpath_tags(t, employee_example, `//employee/ancestor::empinfo`, "empinfo")
// Test Panic
//test_xpath_elements(t, employee_example, `//ancestor::name`, 4, 9, 14)
}
func Test_ancestor_predicate(t *testing.T) {
doc := createElement(0, "",
createElement(1, "html",
createElement(2, "body",
createElement(3, "h1"),
createElement(4, "section",
createElement(5, "div",
createElement(6, "section",
createElement(7, "div",
createElement(8, "span"),
),
),
),
),
createElement(9, "section",
createElement(10, "div",
createElement(11, "section",
createElement(12, "div",
createElement(13, "span"),
),
),
),
),
),
),
)
test_xpath_elements(t, doc, `//span/ancestor::*`, 7, 6, 5, 4, 2, 1, 12, 11, 10, 9)
test_xpath_elements(t, doc, `//span/ancestor::section`, 6, 4, 11, 9)
test_xpath_elements(t, doc, `//span/ancestor::section[1]`, 6, 11)
test_xpath_elements(t, doc, `//span/ancestor::section[2]`, 4, 9)
}
func Test_ancestor_or_self(t *testing.T) {
// Expected the value is [2, 3, 8, 13], but got [3, 2, 8, 13]
test_xpath_elements(t, employee_example, `//employee/ancestor-or-self::*`, 3, 2, 8, 13)
test_xpath_elements(t, employee_example, `//name/ancestor-or-self::employee`, 3, 8, 13)
}
func Test_parent(t *testing.T) {
test_xpath_elements(t, employee_example, `//name/parent::*`, 3, 8, 13)
test_xpath_elements(t, employee_example, `//name/parent::employee`, 3, 8, 13)
}
func Test_attribute(t *testing.T) {
test_xpath_values(t, employee_example, `//attribute::id`, "1", "2", "3")
test_xpath_count(t, employee_example, `//attribute::*`, 9)
// test failed
//test_xpath_tags(t, employee_example, `//attribute::*[1]`, "id", "discipline", "id", "from", "discipline", "id", "discipline")
// test failed(random): the return values is expected but the order of value is random.
//test_xpath_tags(t, employee_example, `//attribute::*`, "id", "discipline", "experience", "id", "from", "discipline", "experience", "id", "discipline")
}
func Test_following(t *testing.T) {
test_xpath_elements(t, employee_example, `//employee[@id=1]/following::*`, 8, 9, 10, 11, 13, 14, 15, 16)
}
func Test_following_sibling(t *testing.T) {
test_xpath_elements(t, employee_example, `//employee[@id=1]/following-sibling::*`, 8, 13)
test_xpath_elements(t, employee_example, `//employee[@id=1]/following-sibling::employee`, 8, 13)
}
func Test_preceding(t *testing.T) {
//testXPath3(t, html, "//li[last()]/preceding-sibling::*[2]", selectNode(html, "//li[position()=2]"))
//testXPath3(t, html, "//li/preceding::*[1]", selectNode(html, "//h1"))
test_xpath_elements(t, employee_example, `//employee[@id=3]/preceding::*`, 8, 9, 10, 11, 3, 4, 5, 6)
}
func Test_preceding_sibling(t *testing.T) {
test_xpath_elements(t, employee_example, `//employee[@id=3]/preceding-sibling::*`, 8, 3)
}
func Test_namespace(t *testing.T) {
// TODO
}
golang-github-antchfx-xpath-1.3.5/xpath_expression_test.go 0000664 0000000 0000000 00000014202 15052615265 0024021 0 ustar 00root root 0000000 0000000 package xpath
import (
"testing"
)
func Test_descendant_issue(t *testing.T) {
// Issue #93 https://github.com/antchfx/xpath/issues/93
/*
*/
doc := createNode("", RootNode)
div := doc.createChildNode("div", ElementNode)
div.lines = 1
div.addAttribute("id", "wrapper")
span := div.createChildNode("span", ElementNode)
span.lines = 2
span.createChildNode("span one", TextNode)
div = div.createChildNode("div", ElementNode)
div.lines = 3
span = div.createChildNode("span", ElementNode)
span.lines = 4
span.createChildNode("span two", TextNode)
test_xpath_elements(t, doc, `//div[@id='wrapper']/descendant::span[1]`, 2)
test_xpath_elements(t, doc, `//div[@id='wrapper']//descendant::span[1]`, 2, 4)
}
// https://github.com/antchfx/htmlquery/issues/52
func TestRelativePaths(t *testing.T) {
test_xpath_elements(t, book_example, `//bookstore`, 2)
test_xpath_elements(t, book_example, `//book`, 3, 9, 15, 25)
test_xpath_elements(t, book_example, `//bookstore/book`, 3, 9, 15, 25)
test_xpath_tags(t, book_example, `//book/..`, "bookstore")
test_xpath_elements(t, book_example, `//book[@category="cooking"]/..`, 2)
test_xpath_elements(t, book_example, `//book/year[text() = 2005]/../..`, 2) // bookstore
test_xpath_elements(t, book_example, `//book/year/../following-sibling::*`, 9, 15, 25)
test_xpath_count(t, book_example, `//bookstore/book/*`, 20)
test_xpath_tags(t, html_example, "//title/../..", "html")
test_xpath_elements(t, html_example, "//ul/../p", 19)
}
func TestAbsolutePaths(t *testing.T) {
test_xpath_elements(t, book_example, `bookstore`, 2)
test_xpath_elements(t, book_example, `bookstore/book`, 3, 9, 15, 25)
test_xpath_elements(t, book_example, `(bookstore/book)`, 3, 9, 15, 25)
test_xpath_elements(t, book_example, `bookstore/book[2]`, 9)
test_xpath_elements(t, book_example, `bookstore/book[last()]`, 25)
test_xpath_elements(t, book_example, `bookstore/book[last()]/title`, 26)
test_xpath_values(t, book_example, `/bookstore/book[last()]/title/text()`, "Learning XML")
test_xpath_values(t, book_example, `/bookstore/book[@category = "children"]/year`, "2005")
test_xpath_elements(t, book_example, `bookstore/book/..`, 2)
test_xpath_elements(t, book_example, `/bookstore/*`, 3, 9, 15, 25)
test_xpath_elements(t, book_example, `/bookstore/*/title`, 4, 10, 16, 26)
}
func TestAttributes(t *testing.T) {
test_xpath_tags(t, html_example.FirstChild, "@*", "lang")
test_xpath_count(t, employee_example, `//@*`, 9)
test_xpath_values(t, employee_example, `//@discipline`, "web", "DBA", "appdev")
test_xpath_count(t, employee_example, `//employee/@id`, 3)
}
func TestExpressions(t *testing.T) {
test_xpath_elements(t, book_example, `//book[@category = "cooking"] | //book[@category = "children"]`, 3, 9)
test_xpath_elements(t, book_example, `//book[@category = "web"] and //book[price = "39.95"]`, 25)
test_xpath_count(t, html_example, `//ul/*`, 3)
test_xpath_count(t, html_example, `//ul/*/a`, 3)
// Sequence
//
// table/tbody/tr/td/(para, .[not(para)], ..)
}
func TestSequence(t *testing.T) {
// `//table/tbody/tr/td/(para, .[not(para)],..)`
test_xpath_count(t, html_example, `//body/(h1, h2, p)`, 2)
test_xpath_count(t, html_example, `//body/(h1, h2, p, ..)`, 3)
}
func TestLatinAttributesInXPath(t *testing.T) {
doc := createNode("", RootNode)
div := doc.createChildNode("div", ElementNode)
div.addAttribute("language", "english")
div.lines = 1
test_xpath_elements(t, doc, `//div[@language='english']`, 1)
}
func TestCyrillicAttributesInXPath(t *testing.T) {
doc := createNode("", RootNode)
div := doc.createChildNode("div", ElementNode)
div.addAttribute("язык", "русский")
div.lines = 1
test_xpath_elements(t, doc, `//div[@язык='русский']`, 1)
}
func TestGreekAttributesInXPath(t *testing.T) {
doc := createNode("", RootNode)
div := doc.createChildNode("div", ElementNode)
div.addAttribute("γλώσσα", "ελληνικά")
div.lines = 1
test_xpath_elements(t, doc, `//div[@γλώσσα='ελληνικά']`, 1)
}
func TestCyrillicAndGreekAttributesMixedInXPath(t *testing.T) {
doc := createNode("", RootNode)
div := doc.createChildNode("div", ElementNode)
div.addAttribute("язык", "русский")
div.addAttribute("γλώσσα", "ελληνικά")
div.lines = 1
test_xpath_elements(t, doc, `//div[@язык='русский' and @γλώσσα='ελληνικά']`, 1)
}
func TestCyrillicAttributesInXPath_NoMatch(t *testing.T) {
doc := createNode("", RootNode)
div := doc.createChildNode("div", ElementNode)
div.addAttribute("язык", "русский")
div.lines = 1
test_xpath_elements(t, doc, `//div[@язык='английский']`)
}
func TestGreekAttributesInXPath_NoMatch(t *testing.T) {
doc := createNode("", RootNode)
div := doc.createChildNode("div", ElementNode)
div.addAttribute("γλώσσα", "ελληνικά")
div.lines = 1
test_xpath_elements(t, doc, `//div[@γλώσσα='αγγλικά']`)
}
func TestNonEnglishExpression(t *testing.T) {
doc := createNode("", RootNode)
n_1 := doc.createChildNode("Σειρά", ElementNode)
n_1.lines = 1
n_2 := n_1.createChildNode("ελληνικά", ElementNode)
n_2.lines = 2
n_2.createChildNode("hello", TextNode)
test_xpath_elements(t, doc, "//Σειρά", 1)
test_xpath_values(t, doc, "//Σειρά/ελληνικά", "hello")
}
func TestChineseCharactersExpression(t *testing.T) {
doc := createNode("", RootNode)
n := doc.createChildNode("中文", ElementNode)
n.createChildNode("你好世界", TextNode)
test_xpath_values(t, doc, "//中文", "你好世界")
}
func TestBUG_104(t *testing.T) {
// BUG https://github.com/antchfx/xpath/issues/104
test_xpath_count(t, book_example, `//author[1]`, 4)
test_xpath_values(t, book_example, `//author[1]/text()`, "Giada De Laurentiis", "J K. Rowling", "James McGovern", "Erik T. Ray")
}
func TestNonEnglishPredicateExpression(t *testing.T) {
// https://github.com/antchfx/xpath/issues/106
doc := createNode("", RootNode)
n := doc.createChildNode("h1", ElementNode)
n.addAttribute("id", "断点")
n.createChildNode("Hello,World!", TextNode)
test_xpath_count(t, doc, "//h1[@id='断点']", 1)
}
golang-github-antchfx-xpath-1.3.5/xpath_function_test.go 0000664 0000000 0000000 00000035157 15052615265 0023463 0 ustar 00root root 0000000 0000000 package xpath
import (
"math"
"testing"
)
// Some test examples from http://zvon.org/comp/r/ref-XPath_2.html
func Test_func_boolean(t *testing.T) {
test_xpath_eval(t, empty_example, `true()`, true)
test_xpath_eval(t, empty_example, `false()`, false)
test_xpath_eval(t, empty_example, `boolean(0)`, false)
test_xpath_eval(t, empty_example, `boolean(null)`, false)
test_xpath_eval(t, empty_example, `boolean(1)`, true)
test_xpath_eval(t, empty_example, `boolean(2)`, true)
test_xpath_eval(t, empty_example, `boolean(true)`, false)
test_xpath_eval(t, empty_example, `boolean(1 > 2)`, false)
test_xpath_eval(t, book_example, `boolean(//*[@lang])`, true)
test_xpath_eval(t, book_example, `boolean(//*[@x])`, false)
}
func Test_func_name(t *testing.T) {
test_xpath_eval(t, html_example, `name(//html/@lang)`, "lang")
test_xpath_eval(t, html_example, `name(html/head/title)`, "title")
test_xpath_count(t, html_example, `//*[name() = "li"]`, 3)
}
func Test_func_not(t *testing.T) {
//test_xpath_eval(t, empty_example, `not(0)`, true)
//test_xpath_eval(t, empty_example, `not(1)`, false)
test_xpath_elements(t, employee_example, `//employee[not(@id = "1")]`, 8, 13)
test_xpath_elements(t, book_example, `//book[not(year = 2005)]`, 15, 25)
test_xpath_count(t, book_example, `//book[not(title)]`, 0)
}
func Test_func_ceiling_floor(t *testing.T) {
test_xpath_eval(t, empty_example, "ceiling(5.2)", float64(6))
test_xpath_eval(t, empty_example, "floor(5.2)", float64(5))
}
func Test_func_concat(t *testing.T) {
test_xpath_eval(t, empty_example, `concat("1", "2", "3")`, "123")
//test_xpath_eval(t, empty_example, `concat("Ciao!", ())`, "Ciao!")
test_xpath_eval(t, book_example, `concat(//book[1]/title, ", ", //book[1]/year)`, "Everyday Italian, 2005")
result := concatFunc(testQuery("a"), testQuery("b"))(nil, nil).(string)
assertEqual(t, result, "ab")
}
func Test_func_contains(t *testing.T) {
test_xpath_eval(t, empty_example, `contains("tattoo", "t")`, true)
test_xpath_eval(t, empty_example, `contains("tattoo", "T")`, false)
test_xpath_eval(t, empty_example, `contains("tattoo", "ttt")`, false)
//test_xpath_eval(t, empty_example, `contains("", ())`, true)
test_xpath_elements(t, book_example, `//book[contains(title, "Potter")]`, 9)
test_xpath_elements(t, book_example, `//book[contains(year, "2005")]`, 3, 9)
assertPanic(t, func() { selectNode(html_example, "//*[contains(0, 0)]") })
}
func Test_func_count(t *testing.T) {
test_xpath_eval(t, book_example, `count(//book)`, float64(4))
test_xpath_eval(t, book_example, `count(//book[3]/author)`, float64(5))
}
func Test_func_ends_with(t *testing.T) {
test_xpath_eval(t, empty_example, `ends-with("tattoo", "tattoo")`, true)
test_xpath_eval(t, empty_example, `ends-with("tattoo", "atto")`, false)
test_xpath_elements(t, book_example, `//book[ends-with(@category,'ing')]`, 3)
test_xpath_elements(t, book_example, `//book[ends-with(./price,'.99')]`, 9, 15)
assertPanic(t, func() { selectNode(html_example, `//*[ends-with(0, 0)]`) }) // arg must be start with string
assertPanic(t, func() { selectNode(html_example, `//*[ends-with(name(), 0)]`) })
}
func Test_func_last(t *testing.T) {
test_xpath_elements(t, book_example, `//bookstore[last()]`, 2)
test_xpath_elements(t, book_example, `//bookstore/book[last()]`, 25)
test_xpath_elements(t, book_example, `(//bookstore/book)[last()]`, 25)
//https: //github.com/antchfx/xpath/issues/76
test_xpath_elements(t, book_example, `(//bookstore/book[year = 2005])[last()]`, 9)
test_xpath_elements(t, book_example, `//bookstore/book[year = 2005][last()]`, 9)
test_xpath_elements(t, html_example, `//ul/li[last()]`, 15)
test_xpath_elements(t, html_example, `(//ul/li)[last()]`, 15)
}
func Test_func_local_name(t *testing.T) {
test_xpath_eval(t, book_example, `local-name(bookstore)`, "bookstore")
test_xpath_eval(t, mybook_example, `local-name(//mybook:book)`, "book")
}
func Test_func_starts_with(t *testing.T) {
test_xpath_eval(t, employee_example, `starts-with("tattoo", "tat")`, true)
test_xpath_eval(t, employee_example, `starts-with("tattoo", "att")`, false)
test_xpath_elements(t, book_example, `//book[starts-with(title,'Everyday')]`, 3)
assertPanic(t, func() { selectNode(html_example, `//*[starts-with(0, 0)]`) })
assertPanic(t, func() { selectNode(html_example, `//*[starts-with(name(), 0)]`) })
}
func Test_func_string(t *testing.T) {
test_xpath_eval(t, empty_example, `string(1.23)`, "1.23")
test_xpath_eval(t, empty_example, `string(3)`, "3")
test_xpath_eval(t, book_example, `string(//book/@category)`, "cooking")
}
func Test_func_string_empty(t *testing.T) {
// https://github.com/antchfx/xpath/issues/113
// Create the equivalent of hi
using TNode
doc := createNode("", RootNode)
div := doc.createChildNode("div", ElementNode)
div.lines = 1 // Assign a line number for the test helper
div.createChildNode("hi", TextNode)
// Use the existing test helper to evaluate the XPath
// The expression selects the div if its string value ("hi") is true in a boolean context.
test_xpath_values(t, doc, `//div[string()]`, "hi")
}
func Test_func_string_join(t *testing.T) {
//(t, empty_example, `string-join(('Now', 'is', 'the', 'time', '...'), '')`, "Now is the time ...")
test_xpath_eval(t, empty_example, `string-join("some text", ";")`, "some text")
test_xpath_eval(t, book_example, `string-join(//book/@category, ";")`, "cooking;children;web;web")
}
func Test_func_string_length(t *testing.T) {
test_xpath_eval(t, empty_example, `string-length("Harp not on that string, madam; that is past.")`, float64(45))
test_xpath_eval(t, empty_example, `string-length(normalize-space(' abc '))`, float64(3))
test_xpath_eval(t, html_example, `string-length(//title/text())`, float64(len("My page")))
test_xpath_eval(t, html_example, `string-length(//html/@lang)`, float64(len("en")))
test_xpath_count(t, employee_example, `//employee[string-length(@id) > 0]`, 3) // = //employee[@id]
}
func Test_func_substring(t *testing.T) {
test_xpath_eval(t, empty_example, `substring("motor car", 6)`, " car")
test_xpath_eval(t, empty_example, `substring("metadata", 4, 3)`, "ada")
test_xpath_eval(t, empty_example, `substring("12345", 5, -3)`, "")
test_xpath_eval(t, empty_example, `substring("12345", 1.5, 2.6)`, "234")
test_xpath_eval(t, empty_example, `substring("12345", 0, 3)`, "12")
test_xpath_eval(t, empty_example, `substring("12345", 5, -3)`, "")
test_xpath_eval(t, empty_example, `substring("12345", 0, 5)`, "1234")
test_xpath_eval(t, empty_example, `substring("12345", 1, 5)`, "12345")
test_xpath_eval(t, empty_example, `substring("12345", 1, 6)`, "12345")
test_xpath_eval(t, html_example, `substring(//title/child::node(), 1)`, "My page")
//assertPanic(t, func() { selectNode(empty_example, `substring("12345", 5, "")`) })
}
func Test_func_substring_after(t *testing.T) {
test_xpath_eval(t, empty_example, `substring-after("tattoo", "tat")`, "too")
test_xpath_eval(t, empty_example, `substring-after("tattoo", "tattoo")`, "")
}
func Test_func_substring_before(t *testing.T) {
test_xpath_eval(t, empty_example, `substring-before("tattoo", "attoo")`, "t")
test_xpath_eval(t, empty_example, `substring-before("tattoo", "tatto")`, "")
}
func Test_func_sum(t *testing.T) {
test_xpath_eval(t, empty_example, `sum(1 + 2)`, float64(3))
test_xpath_eval(t, empty_example, `sum(1.1 + 2)`, float64(3.1))
test_xpath_eval(t, book_example, `sum(//book/price)`, float64(149.93))
test_xpath_elements(t, book_example, `//book[sum(./price) > 40]`, 15)
assertPanic(t, func() { selectNode(html_example, `//title[sum('Hello') = 0]`) })
}
func Test_func_translate(t *testing.T) {
test_xpath_eval(t, empty_example, `translate("bar","abc","ABC")`, "BAr")
test_xpath_eval(t, empty_example, `translate("--aaa--","abc-","ABC")`, "AAA")
test_xpath_eval(t, empty_example, `translate("abcdabc", "abc", "AB")`, "ABdAB")
test_xpath_eval(t, empty_example, `translate('The quick brown fox', 'brown', 'red')`, "The quick red fdx")
}
func Test_func_matches(t *testing.T) {
test_xpath_eval(t, empty_example, `matches("abracadabra", "bra")`, true)
test_xpath_eval(t, empty_example, `matches("abracadabra", "(?i)^A.*A$")`, true)
test_xpath_eval(t, empty_example, `matches("abracadabra", "^a.*a$")`, true)
test_xpath_eval(t, empty_example, `matches("abracadabra", "^bra")`, false)
assertPanic(t, func() { selectNode(html_example, `//*[matches()]`) }) // arg len check failure
assertPanic(t, func() { selectNode(html_example, "//*[matches(substring(), 0)]") }) // first arg processing failure
assertPanic(t, func() { selectNode(html_example, "//*[matches(@href, substring())]") }) // second arg processing failure
assertPanic(t, func() { selectNode(html_example, "//*[matches(@href, 0)]") }) // second arg not string
assertPanic(t, func() { selectNode(html_example, "//*[matches(@href, '[invalid')]") }) // second arg invalid regexp
// testing unexpected the regular expression.
_, err := Compile(`//*[matches(., '^[\u0621-\u064AA-Za-z\-]+')]`)
assertErr(t, err)
_, err = Compile(`//*[matches(., '//*[matches(., '\w+`)
assertErr(t, err)
}
func Test_func_number(t *testing.T) {
test_xpath_eval(t, empty_example, `number(10)`, float64(10))
test_xpath_eval(t, empty_example, `number(1.11)`, float64(1.11))
test_xpath_eval(t, empty_example, `number("10") > 10`, false)
test_xpath_eval(t, empty_example, `number("10") = 10`, true)
test_xpath_eval(t, empty_example, `number("123") < 1000`, true)
test_xpath_eval(t, empty_example, `number(//non-existent-node) = 0`, false)
assertTrue(t, math.IsNaN(MustCompile(`number(//non-existent-node)`).Evaluate(createNavigator(empty_example)).(float64)))
assertTrue(t, math.IsNaN(MustCompile(`number("123a")`).Evaluate(createNavigator(empty_example)).(float64)))
}
func Test_func_position(t *testing.T) {
test_xpath_elements(t, book_example, `//book[position() = 1]`, 3)
test_xpath_elements(t, book_example, `//book[(position() mod 2) = 0]`, 9, 25)
test_xpath_elements(t, book_example, `//book[position() = last()]`, 25)
test_xpath_elements(t, book_example, `//book/*[position() = 1]`, 4, 10, 16, 26)
// Test Failed
//test_xpath_elements(t, book_example, `(//book/title)[position() = 1]`, 3)
}
func Test_func_replace(t *testing.T) {
test_xpath_eval(t, empty_example, `replace('aa-bb-cc','bb','ee')`, "aa-ee-cc")
test_xpath_eval(t, empty_example, `replace("abracadabra", "bra", "*")`, "a*cada*")
test_xpath_eval(t, empty_example, `replace("abracadabra", "a", "")`, "brcdbr")
// The below xpath expressions is not supported yet
//
test_xpath_eval(t, empty_example, `replace("abracadabra", "a.*a", "*")`, "*")
test_xpath_eval(t, empty_example, `replace("abracadabra", "a.*?a", "*")`, "*c*bra")
// test_xpath_eval(t, empty_example, `replace("abracadabra", ".*?", "$1")`, "*c*bra") // error, because the pattern matches the zero-length string
test_xpath_eval(t, empty_example, `replace("AAAA", "A+", "b")`, "b")
test_xpath_eval(t, empty_example, `replace("AAAA", "A+?", "b")`, "bbbb")
test_xpath_eval(t, empty_example, `replace("darted", "^(.*?)d(.*)$", "$1c$2")`, "carted")
test_xpath_eval(t, empty_example, `replace("abracadabra", "a(.)", "a$1$1")`, "abbraccaddabbra")
test_xpath_eval(t, empty_example, `replace("abcd", "(ab)|(a)", "[1=$1][2=$2]")`, "[1=ab][2=]cd")
test_xpath_eval(t, empty_example, `replace("1/1/c11/1", "(.*)/[^/]+$", "$1")`, "1/1/c11")
test_xpath_eval(t, empty_example, `replace("A/B/C/D/E/F/G/H/I/J/K/L", "([^/]*)/([^/]*)/([^/]*)/([^/]*)/([^/]*)/([^/]*)/([^/]*)/([^/]*)/([^/]*)/(.*)", "$1-$2-$3-$4-$5-$6-$7-$8-$9-$10")`, "A-B-C-D-E-F-G-H-I-J/K/L")
}
func Test_func_reverse(t *testing.T) {
//test_xpath_eval(t, employee_example, `reverse(("hello"))`, "hello") // Not passed
test_xpath_elements(t, employee_example, `reverse(//employee)`, 13, 8, 3)
test_xpath_elements(t, employee_example, `//employee[reverse(.) = reverse(.)]`, 3, 8, 13)
assertPanic(t, func() { selectNode(html_example, "reverse(concat())") }) // invalid node-sets argument.
assertPanic(t, func() { selectNode(html_example, "reverse()") }) // missing node-sets argument.
}
func Test_func_round(t *testing.T) {
test_xpath_eval(t, employee_example, `round(2.5)`, 3) // int
test_xpath_eval(t, employee_example, `round(2.5)`, 3)
test_xpath_eval(t, employee_example, `round(2.4999)`, 2)
}
func Test_func_namespace_uri(t *testing.T) {
test_xpath_eval(t, mybook_example, `namespace-uri(//mybook:book)`, "http://www.contoso.com/books")
test_xpath_elements(t, mybook_example, `//*[namespace-uri()='http://www.contoso.com/books']`, 3, 9)
}
func Test_func_normalize_space(t *testing.T) {
const testStr = "\t \rloooooooonnnnnnngggggggg \r \n tes \u00a0 t strin \n\n \r g "
const expectedStr = `loooooooonnnnnnngggggggg tes t strin g`
test_xpath_eval(t, empty_example, `normalize-space("`+testStr+`")`, expectedStr)
test_xpath_eval(t, empty_example, `normalize-space(' abc ')`, "abc")
n := selectNode(employee_example, `//employee[@id="1"]/name`)
test_xpath_eval(t, n, `normalize-space()`, "Opal Kole")
test_xpath_eval(t, n, `normalize-space(.)`, "Opal Kole")
test_xpath_eval(t, book_example, `normalize-space(//book/title)`, "Everyday Italian")
test_xpath_eval(t, book_example, `normalize-space(//book[1]/title)`, "Everyday Italian")
}
func Test_func_lower_case(t *testing.T) {
test_xpath_eval(t, empty_example, `lower-case("ABc!D")`, "abc!d")
test_xpath_count(t, employee_example, `//name[@from="ca"]`, 0)
test_xpath_elements(t, employee_example, `//name[lower-case(@from) = "ca"]`, 9)
//test_xpath_eval(t, employee_example, `//employee/name/lower-case(text())`, "opal kole", "max miller", "beccaa moss")
}
func Benchmark_NormalizeSpaceFunc(b *testing.B) {
b.ReportAllocs()
const strForNormalization = "\t \rloooooooonnnnnnngggggggg \r \n tes \u00a0 t strin \n\n \r g "
for i := 0; i < b.N; i++ {
_ = normalizespaceFunc(testQuery(strForNormalization))(nil, nil)
}
}
func Benchmark_ConcatFunc(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = concatFunc(testQuery("a"), testQuery("b"))(nil, nil)
}
}
func Benchmark_GetHashCode(b *testing.B) {
// Create a more complex test node that will actually go through the full getHashCode paths
doc := createNavigator(book_example)
doc.MoveToRoot()
doc.MoveToChild() // Move to the first element
doc.MoveToChild() // Deeper
// Find a node with attributes
var node NodeNavigator
for {
if doc.NodeType() == AttributeNode || doc.NodeType() == TextNode || doc.NodeType() == CommentNode {
node = doc.Copy()
break
}
if !doc.MoveToNext() {
if !doc.MoveToChild() {
// If we can't find a suitable node, default to using the first element
doc.MoveToRoot()
doc.MoveToChild()
node = doc.Copy()
break
}
}
}
b.Run("getHashCode", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
n := node.Copy()
_ = getHashCode(n)
}
})
}
golang-github-antchfx-xpath-1.3.5/xpath_predicate_test.go 0000664 0000000 0000000 00000005721 15052615265 0023570 0 ustar 00root root 0000000 0000000 package xpath
import (
"testing"
)
func TestLogicals(t *testing.T) {
test_xpath_elements(t, book_example, `//book[1 + 1]`, 9)
test_xpath_elements(t, book_example, `//book[1 * 2]`, 9)
test_xpath_elements(t, book_example, `//book[5 div 2]`, 9) // equal to `//book[2]`
test_xpath_elements(t, book_example, `//book[3 div 2]`, 3)
test_xpath_elements(t, book_example, `//book[3 - 2]`, 3)
test_xpath_elements(t, book_example, `//book[price > 35]`, 15, 25)
test_xpath_elements(t, book_example, `//book[price >= 30]`, 3, 15, 25)
test_xpath_elements(t, book_example, `//book[price < 30]`, 9)
test_xpath_elements(t, book_example, `//book[price <= 30]`, 3, 9)
test_xpath_elements(t, book_example, `//book[count(author) > 1]`, 15)
test_xpath_elements(t, book_example, `//book[position() mod 2 = 0]`, 9, 25)
}
func TestPositions(t *testing.T) {
test_xpath_elements(t, employee_example, `/empinfo/employee[2]`, 8)
test_xpath_elements(t, employee_example, `//employee[position() = 2]`, 8)
test_xpath_elements(t, employee_example, `/empinfo/employee[2]/name`, 9)
test_xpath_elements(t, employee_example, `//employee[position() > 1]`, 8, 13)
test_xpath_elements(t, employee_example, `//employee[position() <= 2]`, 3, 8)
test_xpath_elements(t, employee_example, `//employee[last()]`, 13)
test_xpath_elements(t, employee_example, `//employee[position() = last()]`, 13)
test_xpath_elements(t, book_example, `//book[@category = "web"][2]`, 25)
test_xpath_elements(t, book_example, `(//book[@category = "web"])[2]`, 25)
}
func TestPredicates(t *testing.T) {
test_xpath_elements(t, employee_example, `//employee[name]`, 3, 8, 13)
test_xpath_elements(t, employee_example, `/empinfo/employee[@id]`, 3, 8, 13)
test_xpath_elements(t, book_example, `//book[@category = "web"]`, 15, 25)
test_xpath_elements(t, book_example, `//book[author = "J K. Rowling"]`, 9)
test_xpath_elements(t, book_example, `//book[./author/text() = "J K. Rowling"]`, 9)
test_xpath_elements(t, book_example, `//book[year = 2005]`, 3, 9)
test_xpath_elements(t, book_example, `//year[text() = 2005]`, 6, 12)
test_xpath_elements(t, employee_example, `/empinfo/employee[1][@id=1]`, 3)
test_xpath_elements(t, employee_example, `/empinfo/employee[@id][2]`, 8)
}
func TestOperators(t *testing.T) {
test_xpath_elements(t, employee_example, `//designation[@discipline and @experience]`, 5, 10)
test_xpath_elements(t, employee_example, `//designation[@discipline or @experience]`, 5, 10, 15)
test_xpath_elements(t, employee_example, `//designation[@discipline | @experience]`, 5, 10, 15)
test_xpath_elements(t, employee_example, `/empinfo/employee[@id != "2" ]`, 3, 13)
test_xpath_elements(t, employee_example, `/empinfo/employee[@id and @id = "2"]`, 8)
test_xpath_elements(t, employee_example, `/empinfo/employee[@id = "1" or @id = "2"]`, 3, 8)
}
func TestNestedPredicates(t *testing.T) {
test_xpath_elements(t, employee_example, `//employee[./name[@from]]`, 8)
test_xpath_elements(t, employee_example, `//employee[.//name[@from = "CA"]]`, 8)
}
golang-github-antchfx-xpath-1.3.5/xpath_test.go 0000664 0000000 0000000 00000056203 15052615265 0021551 0 ustar 00root root 0000000 0000000 package xpath
import (
"bytes"
"fmt"
"math"
"sort"
"strings"
"testing"
)
var (
employee_example = createEmployeeExample()
book_example = createBookExample()
html_example = createHtmlExample()
empty_example = createNode("", RootNode)
mybook_example = createMyBookExample()
)
type testQuery string
func (t testQuery) Select(_ iterator) NodeNavigator {
panic("implement me")
}
func (t testQuery) Clone() query {
return t
}
func (t testQuery) Evaluate(_ iterator) interface{} {
return string(t)
}
func (t testQuery) ValueType() resultType {
return xpathResultType.Any
}
func (t testQuery) Properties() queryProp {
return queryProps.None
}
func test_xpath_elements(t *testing.T, root *TNode, expr string, expected ...int) {
result := selectNodes(root, expr)
assertEqual(t, len(expected), len(result))
for i := 0; i < len(expected); i++ {
assertEqual(t, expected[i], result[i].lines)
}
}
func test_xpath_values(t testing.TB, root *TNode, expr string, expected ...string) {
result := selectNodes(root, expr)
assertEqual(t, len(expected), len(result))
for i := 0; i < len(expected); i++ {
assertEqual(t, expected[i], result[i].Value())
}
}
func test_xpath_tags(t *testing.T, root *TNode, expr string, expected ...string) {
result := selectNodes(root, expr)
assertEqual(t, len(expected), len(result))
for i := 0; i < len(expected); i++ {
assertEqual(t, expected[i], result[i].Data)
}
}
func test_xpath_count(t *testing.T, root *TNode, expr string, expected int) {
result := selectNodes(root, expr)
assertEqual(t, expected, len(result))
}
func test_xpath_eval(t *testing.T, root *TNode, expr string, expected ...interface{}) {
e, err := Compile(expr)
assertNoErr(t, err)
v := e.Evaluate(createNavigator(root))
// if is a node-set
if iter, ok := v.(*NodeIterator); ok {
got := iterateNavs(iter)
assertEqual(t, len(expected), len(got))
for i := 0; i < len(expected); i++ {
assertEqual(t, expected[i], got[i])
}
return
}
assertEqual(t, expected[0], v)
}
func Test_Predicates_MultiParent(t *testing.T) {
// https://github.com/antchfx/xpath/issues/75
/*
field1
field2
31854
159773
field3
field4
1234
567
*/
doc := createNode("", RootNode)
measCollecFile := doc.createChildNode("measCollecFile", ElementNode)
measData := measCollecFile.createChildNode("measData", ElementNode)
data := []struct {
measType map[string]string
measValue map[string]string
}{
{measType: map[string]string{"1": "field1", "2": "field2"}, measValue: map[string]string{"1": "31854", "2": "159773"}},
{measType: map[string]string{"1": "field3", "2": "field4"}, measValue: map[string]string{"1": "1234", "2": "567"}},
}
for j := 0; j < len(data); j++ {
d := data[j]
measInfo := measData.createChildNode("measInfo", ElementNode)
measType := measInfo.createChildNode("measType", ElementNode)
var keys []string
for k := range d.measType {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
measType.addAttribute("p", k)
measType.createChildNode(d.measType[k], TextNode)
}
measValue := measInfo.createChildNode("measValue", ElementNode)
keys = make([]string, 0)
for k := range d.measValue {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
r := measValue.createChildNode("r", ElementNode)
r.addAttribute("p", k)
r.createChildNode(d.measValue[k], TextNode)
}
}
test_xpath_values(t, doc, `//r[@p=../../measType/@p]`, "31854", "159773", "1234", "567")
}
func TestCompile(t *testing.T) {
var err error
_, err = Compile("//a")
assertNil(t, err)
_, err = Compile("//a[id=']/span")
assertErr(t, err)
_, err = Compile("//ul/li/@class")
assertNil(t, err)
_, err = Compile("/a/b/(c, .[not(c)])")
assertNil(t, err)
_, err = Compile("/pre:foo")
assertNil(t, err)
}
func TestInvalidXPath(t *testing.T) {
var err error
_, err = Compile("()")
assertErr(t, err)
_, err = Compile("(1,2,3)")
assertErr(t, err)
}
func TestCompileWithNS(t *testing.T) {
_, err := CompileWithNS("/foo", nil)
assertNil(t, err)
_, err = CompileWithNS("/foo", map[string]string{})
assertNil(t, err)
_, err = CompileWithNS("/foo", map[string]string{"a": "b"})
assertNil(t, err)
_, err = CompileWithNS("/a:foo", map[string]string{"a": "b"})
assertNil(t, err)
_, err = CompileWithNS("/u:foo", map[string]string{"a": "b"})
assertErr(t, err)
}
func TestNamespacePrefixQuery(t *testing.T) {
/*
book1
book2
book3
*/
doc := createNode("", RootNode)
books := doc.createChildNode("books", ElementNode)
books.lines = 2
book1 := books.createChildNode("book", ElementNode)
book1.lines = 3
book1.createChildNode("book1", TextNode)
book2 := books.createChildNode("b:book", ElementNode)
book2.lines = 4
book2.addAttribute("xmlns:b", "ns")
book2.createChildNode("book2", TextNode)
book3 := books.createChildNode("c:book", ElementNode)
book3.lines = 5
book3.addAttribute("xmlns:c", "ns")
book3.createChildNode("book3", TextNode)
test_xpath_elements(t, doc, `//b:book`, 4) // expected [4 , 5]
// With namespace bindings:
exp, _ := CompileWithNS("//x:book", map[string]string{"x": "ns"})
nodes := iterateNodes(exp.Select(createNavigator(doc)))
assertEqual(t, 2, len(nodes))
assertEqual(t, "book2", nodes[0].Value())
assertEqual(t, "book3", nodes[1].Value())
}
func TestMustCompile(t *testing.T) {
expr := MustCompile("//")
assertTrue(t, expr != nil)
if wanted := (nopQuery{}); expr.q != wanted {
t.Fatalf("wanted nopQuery object but got %s", expr)
}
iter := expr.Select(createNavigator(html_example))
if iter.MoveNext() {
t.Fatal("should be an empty node list but got one")
}
}
func Test_plusFunc(t *testing.T) {
// 1+1
assertEqual(t, float64(2), plusFunc(nil, float64(1), float64(1)))
// string +
assertEqual(t, float64(2), plusFunc(nil, "1", "1"))
// invalid string
v := plusFunc(nil, "a", 1)
assertTrue(t, math.IsNaN(v.(float64)))
// Nodeset
// TODO
}
func Test_minusFunc(t *testing.T) {
// 1 - 1
assertEqual(t, float64(0), minusFunc(nil, float64(1), float64(1)))
// string
assertEqual(t, float64(0), minusFunc(nil, "1", "1"))
// invalid string
v := minusFunc(nil, "a", 1)
assertTrue(t, math.IsNaN(v.(float64)))
}
func TestNodeType(t *testing.T) {
tests := []struct {
expr string
expected NodeType
}{
{`//employee`, ElementNode},
{`//name[text()]`, ElementNode},
{`//name/text()`, TextNode},
{`//employee/@id`, AttributeNode},
}
for _, test := range tests {
v := selectNode(employee_example, test.expr)
assertTrue(t, v != nil)
assertEqual(t, test.expected, v.Type)
}
doc := createNode("", RootNode)
doc.createChildNode("", CommentNode)
n := selectNode(doc, "//comment()")
assertTrue(t, n != nil)
assertEqual(t, CommentNode, n.Type)
}
func iterateNavs(t *NodeIterator) []*TNodeNavigator {
var nodes []*TNodeNavigator
for t.MoveNext() {
node := t.Current().(*TNodeNavigator)
nodes = append(nodes, node)
}
return nodes
}
func iterateNodes(t *NodeIterator) []*TNode {
var nodes []*TNode
for t.MoveNext() {
n := t.Current().(*TNodeNavigator)
if n.NodeType() == AttributeNode {
childNode := &TNode{
Type: TextNode,
Data: n.Value(),
}
nodes = append(nodes, &TNode{
Parent: n.curr,
Type: AttributeNode,
Data: n.LocalName(),
FirstChild: childNode,
LastChild: childNode,
})
} else {
nodes = append(nodes, n.curr)
}
}
return nodes
}
func selectNode(root *TNode, expr string) *TNode {
list := selectNodes(root, expr)
if len(list) == 0 {
return nil
}
return list[0]
}
func selectNodes(root *TNode, expr string) []*TNode {
t := Select(createNavigator(root), expr)
c := make(map[uint64]bool)
var list []*TNode
for _, n := range iterateNodes(t) {
m := getHashCode(createNavigator(n))
if _, ok := c[m]; ok {
continue
}
c[m] = true
list = append(list, n)
}
return list
}
func joinValues(nodes []*TNode) string {
s := make([]string, 0)
for _, n := range nodes {
s = append(s, n.Value())
}
return strings.Join(s, ",")
}
func createNavigator(n *TNode) *TNodeNavigator {
return &TNodeNavigator{curr: n, root: n, attr: -1}
}
type Attribute struct {
Key, Value string
}
type TNode struct {
Parent, FirstChild, LastChild, PrevSibling, NextSibling *TNode
Type NodeType
Data string
Attr []Attribute
NamespaceURL string
Prefix string
lines int
}
func (n *TNode) Value() string {
if n.Type == TextNode {
return n.Data
}
var buff bytes.Buffer
var output func(*TNode)
output = func(node *TNode) {
if node.Type == TextNode {
buff.WriteString(node.Data)
}
for child := node.FirstChild; child != nil; child = child.NextSibling {
output(child)
}
}
output(n)
return buff.String()
}
// TNodeNavigator is for navigating TNode.
type TNodeNavigator struct {
curr, root *TNode
attr int
}
func (n *TNodeNavigator) NodeType() NodeType {
switch n.curr.Type {
case CommentNode:
return CommentNode
case TextNode:
return TextNode
case ElementNode:
if n.attr != -1 {
return AttributeNode
}
return ElementNode
}
return n.curr.Type
}
func (n *TNodeNavigator) LocalName() string {
if n.attr != -1 {
return n.curr.Attr[n.attr].Key
}
name := n.curr.Data
if strings.Contains(name, ":") {
return strings.Split(name, ":")[1]
}
return name
}
func (n *TNodeNavigator) Prefix() string {
if n.attr == -1 && strings.Contains(n.curr.Data, ":") {
return strings.Split(n.curr.Data, ":")[0]
}
return n.curr.Prefix
}
func (n *TNodeNavigator) NamespaceURL() string {
if n.Prefix() != "" {
for _, a := range n.curr.Attr {
if a.Key == "xmlns:"+n.Prefix() {
return a.Value
}
}
}
return n.curr.NamespaceURL
}
func (n *TNodeNavigator) Value() string {
switch n.curr.Type {
case CommentNode:
return n.curr.Data
case ElementNode:
if n.attr != -1 {
return n.curr.Attr[n.attr].Value
}
var buf bytes.Buffer
node := n.curr.FirstChild
for node != nil {
if node.Type == TextNode {
buf.WriteString(strings.TrimSpace(node.Data))
}
node = node.NextSibling
}
return buf.String()
case TextNode:
return n.curr.Data
}
return ""
}
func (n *TNodeNavigator) Copy() NodeNavigator {
n2 := *n
return &n2
}
func (n *TNodeNavigator) MoveToRoot() {
n.curr = n.root
}
func (n *TNodeNavigator) MoveToParent() bool {
if n.attr != -1 {
n.attr = -1
return true
} else if node := n.curr.Parent; node != nil {
n.curr = node
return true
}
return false
}
func (n *TNodeNavigator) MoveToNextAttribute() bool {
if n.attr >= len(n.curr.Attr)-1 {
return false
}
n.attr++
return true
}
func (n *TNodeNavigator) MoveToChild() bool {
if n.attr != -1 {
return false
}
if node := n.curr.FirstChild; node != nil {
n.curr = node
return true
}
return false
}
func (n *TNodeNavigator) MoveToFirst() bool {
if n.attr != -1 || n.curr.PrevSibling == nil {
return false
}
for {
node := n.curr.PrevSibling
if node == nil {
break
}
n.curr = node
}
return true
}
func (n *TNodeNavigator) String() string {
return n.Value()
}
func (n *TNodeNavigator) MoveToNext() bool {
if n.attr != -1 {
return false
}
if node := n.curr.NextSibling; node != nil {
n.curr = node
return true
}
return false
}
func (n *TNodeNavigator) MoveToPrevious() bool {
if n.attr != -1 {
return false
}
if node := n.curr.PrevSibling; node != nil {
n.curr = node
return true
}
return false
}
func (n *TNodeNavigator) MoveTo(other NodeNavigator) bool {
node, ok := other.(*TNodeNavigator)
if !ok || node.root != n.root {
return false
}
n.curr = node.curr
n.attr = node.attr
return true
}
func createNode(data string, typ NodeType) *TNode {
return &TNode{Data: data, Type: typ, Attr: make([]Attribute, 0)}
}
func (n *TNode) createChildNode(data string, typ NodeType) *TNode {
m := createNode(data, typ)
m.Parent = n
if n.FirstChild == nil {
n.FirstChild = m
} else {
n.LastChild.NextSibling = m
m.PrevSibling = n.LastChild
}
n.LastChild = m
return m
}
func (n *TNode) appendNode(data string, typ NodeType) *TNode {
m := createNode(data, typ)
m.Parent = n.Parent
n.NextSibling = m
m.PrevSibling = n
if n.Parent != nil {
n.Parent.LastChild = m
}
return m
}
func (n *TNode) addAttribute(k, v string) {
n.Attr = append(n.Attr, Attribute{k, v})
}
func (n *TNode) getAttribute(key string) string {
for i := 0; i < len(n.Attr); i++ {
if n.Attr[i].Key == key {
return n.Attr[i].Value
}
}
return ""
}
func createElement(line int, name string, children ...*TNode) *TNode {
nodeType := ElementNode
if name == "" {
nodeType = RootNode
}
n := createNode(name, nodeType)
n.lines = line
for _, c := range children {
c.Parent = n
c.PrevSibling = n.LastChild
if c.PrevSibling == nil {
n.FirstChild = c
} else {
c.PrevSibling.NextSibling = c
}
n.LastChild = c
}
return n
}
func createBookExample() *TNode {
/*
Everyday Italian
Giada De Laurentiis
2005
30.00
Harry Potter
J K. Rowling
2005
29.99
XQuery Kick Start
James McGovern
Per Bothner
Kurt Cagle
James Linn
Vaidyanathan Nagarajan
2003
49.99
Learning XML
Erik T. Ray
2003
39.95
*/
type Element struct {
Data string
Attributes map[string]string
}
books := []struct {
category string
title Element
year int
price float64
authors []string
}{
{
category: "cooking",
title: Element{"Everyday Italian", map[string]string{"lang": "en"}},
year: 2005,
price: 30.00,
authors: []string{"Giada De Laurentiis"},
},
{
category: "children",
title: Element{"Harry Potter", map[string]string{"lang": "en"}},
year: 2005,
price: 29.99,
authors: []string{"J K. Rowling"},
},
{
category: "web",
title: Element{"XQuery Kick Start", map[string]string{"lang": "en"}},
year: 2003,
price: 49.99,
authors: []string{"James McGovern", "Per Bothner", "Kurt Cagle", "James Linn", "Vaidyanathan Nagarajan"},
},
{
category: "web",
title: Element{"Learning XML", map[string]string{"lang": "en"}},
year: 2003,
price: 39.95,
authors: []string{"Erik T. Ray"},
},
}
var lines = 0
doc := createNode("", RootNode)
lines++
bookstore := doc.createChildNode("bookstore", ElementNode)
lines++
bookstore.lines = lines
for i := 0; i < len(books); i++ {
v := books[i]
lines++
book := bookstore.createChildNode("book", ElementNode)
book.lines = lines
lines++
book.addAttribute("category", v.category)
// title
title := book.createChildNode("title", ElementNode)
title.lines = lines
lines++
for k, v := range v.title.Attributes {
title.addAttribute(k, v)
}
title.createChildNode(v.title.Data, TextNode)
// authors
for j := 0; j < len(v.authors); j++ {
author := book.createChildNode("author", ElementNode)
author.lines = lines
lines++
author.createChildNode(v.authors[j], TextNode)
}
// year
year := book.createChildNode("year", ElementNode)
year.lines = lines
lines++
year.createChildNode(fmt.Sprintf("%d", v.year), TextNode)
// price
price := book.createChildNode("price", ElementNode)
price.lines = lines
lines++
price.createChildNode(fmt.Sprintf("%.2f", v.price), TextNode)
}
return doc
}
// The example document from https://way2tutorial.com/xml/xpath-node-test.php
func createEmployeeExample() *TNode {
/*
Opal Kole
Senior Engineer
OpalKole@myemail.com
Max Miller
DBA Engineer
maxmiller@email.com
Beccaa Moss
Application Developer
beccaamoss@email.com
*/
type Element struct {
Data string
Attributes map[string]string
}
var lines = 0
doc := createNode("", RootNode)
lines++ // 1
empinfo := doc.createChildNode("empinfo", ElementNode)
lines++
empinfo.lines = lines
var employees = []struct {
name Element
designation Element
email Element
}{
{
name: Element{Data: "Opal Kole"},
designation: Element{Data: "Senior Engineer", Attributes: map[string]string{
"discipline": "web",
"experience": "3 year",
}},
email: Element{Data: "OpalKole@myemail.com"},
},
{
name: Element{Data: "Max Miller", Attributes: map[string]string{"from": "CA"}},
designation: Element{Data: "DBA Engineer", Attributes: map[string]string{
"discipline": "DBA",
"experience": "2 year",
}},
email: Element{Data: "maxmiller@email.com"},
},
{
name: Element{Data: "Beccaa Moss"},
designation: Element{Data: "Application Developer", Attributes: map[string]string{
"discipline": "appdev",
}},
email: Element{Data: "beccaamoss@email.com"},
},
}
for i := 0; i < len(employees); i++ {
v := employees[i]
lines++
// employee
employee := empinfo.createChildNode("employee", ElementNode)
employee.addAttribute("id", fmt.Sprintf("%d", i+1))
employee.lines = lines
lines++
// name
name := employee.createChildNode("name", ElementNode)
name.createChildNode(v.name.Data, TextNode)
for k, n := range v.name.Attributes {
name.addAttribute(k, n)
}
name.lines = lines
lines++
// designation
designation := employee.createChildNode("designation", ElementNode)
designation.createChildNode(v.designation.Data, TextNode)
for k, n := range v.designation.Attributes {
designation.addAttribute(k, n)
}
designation.lines = lines
lines++
// email
email := employee.createChildNode("email", ElementNode)
email.createChildNode(v.email.Data, TextNode)
for k, n := range v.email.Attributes {
email.addAttribute(k, n)
}
email.lines = lines
// skiping closed tag
lines++
}
return doc
}
func createHtmlExample() *TNode {
/*
My page
Welcome to my page
This is the first paragraph.
*/
lines := 0
doc := createNode("", RootNode)
lines++
xhtml := doc.createChildNode("html", ElementNode)
xhtml.lines = lines
xhtml.addAttribute("lang", "en")
lines++
// head container
head := xhtml.createChildNode("head", ElementNode)
head.lines = lines
lines++
title := head.createChildNode("title", ElementNode)
title.lines = lines
title.createChildNode("My page", TextNode)
lines++
meta := head.createChildNode("meta", ElementNode)
meta.lines = lines
meta.addAttribute("name", "language")
meta.addAttribute("content", "en")
// skip the head
lines++
lines++
body := xhtml.createChildNode("body", ElementNode)
body.lines = lines
lines++
h2 := body.createChildNode("h2", ElementNode)
h2.lines = lines
h2.createChildNode("Welcome to my page", TextNode)
lines++
links := []struct {
text string
href string
}{
{text: "Home", href: "/"},
{text: "About", href: "/About"},
{text: "Login", href: "/account"},
}
ul := body.createChildNode("ul", ElementNode)
ul.lines = lines
lines++
for i := 0; i < len(links); i++ {
link := links[i]
li := ul.createChildNode("li", ElementNode)
li.lines = lines
lines++
a := li.createChildNode("a", ElementNode)
a.lines = lines
a.addAttribute("href", link.href)
a.createChildNode(link.text, TextNode)
lines++
// skip the
lines++
}
// skip the last ul
lines++
p := body.createChildNode("p", ElementNode)
p.lines = lines
lines++
p.createChildNode("This is the first paragraph.", TextNode)
lines++
comment := body.createChildNode("", CommentNode)
comment.lines = lines
lines++
return doc
}
func createMyBookExample() *TNode {
/*
XML Developer's Guide
Gambardella, Matthew
44.95
2000-10-01
Midnight Rain
Ralls, Kim
5.95
2000-12-16
*/
var (
prefix string = "mybook"
namespaceURL string = "http://www.contoso.com/books"
)
lines := 1
doc := createNode("", RootNode)
doc.lines = lines
lines++
books := doc.createChildNode("books", ElementNode)
books.addAttribute("xmlns:mybook", namespaceURL)
books.lines = lines
lines++
data := []struct {
id string
title string
author string
price float64
publish string
}{
{id: "bk101", title: "XML Developer's Guide", author: "Gambardella, Matthew", price: 44.95, publish: "2000-10-01"},
{id: "bk102", title: "Midnight Rain", author: "Ralls, Kim", price: 5.95, publish: "2000-12-16"},
}
for i := 0; i < len(data); i++ {
v := data[i]
book := books.createChildNode("book", ElementNode)
book.addAttribute("id", v.id)
book.Prefix = prefix
book.NamespaceURL = namespaceURL
book.lines = lines
lines++
title := book.createChildNode("title", ElementNode)
title.createChildNode(v.title, TextNode)
title.lines = lines
lines++
author := book.createChildNode("author", ElementNode)
author.createChildNode(v.author, TextNode)
author.lines = lines
lines++
price := book.createChildNode("price", ElementNode)
price.createChildNode(fmt.Sprintf("%.2f", v.price), TextNode)
price.lines = lines
lines++
publish_date := book.createChildNode("publish_date", ElementNode)
publish_date.createChildNode(v.publish, TextNode)
publish_date.lines = lines
lines++
// skip the last of book element
lines++
}
return doc
}