From 6758df2b1d2b252a17980b0f0828b6636215f9c3 Mon Sep 17 00:00:00 2001 From: Paul Lecuq Date: Mon, 19 Aug 2024 03:44:45 +0200 Subject: [PATCH] updated dependencies --- go.mod | 10 +- go.sum | 8 + .../github.com/antchfx/xmlquery/.travis.yml | 17 - vendor/github.com/antchfx/xmlquery/README.md | 65 ++- vendor/github.com/antchfx/xmlquery/books.xml | 121 ---- vendor/github.com/antchfx/xmlquery/node.go | 9 + vendor/github.com/antchfx/xmlquery/parse.go | 105 ++-- vendor/github.com/antchfx/xmlquery/query.go | 2 +- vendor/github.com/antchfx/xpath/.travis.yml | 12 - vendor/github.com/antchfx/xpath/README.md | 134 ++--- vendor/github.com/antchfx/xpath/build.go | 397 +++++++++---- vendor/github.com/antchfx/xpath/func.go | 8 +- vendor/github.com/antchfx/xpath/operator.go | 86 +-- vendor/github.com/antchfx/xpath/query.go | 529 ++++++++++++++++-- vendor/github.com/antchfx/xpath/xpath.go | 12 +- vendor/golang.org/x/net/LICENSE | 4 +- vendor/golang.org/x/net/html/doc.go | 2 +- vendor/golang.org/x/net/html/token.go | 12 +- vendor/golang.org/x/text/LICENSE | 4 +- vendor/modules.txt | 8 +- 20 files changed, 1032 insertions(+), 513 deletions(-) delete mode 100644 vendor/github.com/antchfx/xmlquery/.travis.yml delete mode 100644 vendor/github.com/antchfx/xmlquery/books.xml delete mode 100644 vendor/github.com/antchfx/xpath/.travis.yml diff --git a/go.mod b/go.mod index b19ad45..b8c08ba 100644 --- a/go.mod +++ b/go.mod @@ -1,13 +1,13 @@ module git.paulbsd.com/paulbsd/fuelprices -go 1.21 +go 1.23 require ( - github.com/antchfx/xmlquery v1.3.18 - github.com/antchfx/xpath v1.2.5 // indirect + github.com/antchfx/xmlquery v1.4.1 + github.com/antchfx/xpath v1.3.1 // indirect github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c - golang.org/x/net v0.20.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/net v0.28.0 // indirect + golang.org/x/text v0.17.0 // indirect gopkg.in/ini.v1 v1.67.0 ) diff --git a/go.sum b/go.sum index 1915bae..1298f42 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,8 @@ github.com/antchfx/xmlquery v1.3.15 h1:aJConNMi1sMha5G8YJoAIF5P+H+qG1L73bSItWHo8 github.com/antchfx/xmlquery v1.3.15/go.mod h1:zMDv5tIGjOxY/JCNNinnle7V/EwthZ5IT8eeCGJKRWA= github.com/antchfx/xmlquery v1.3.18 h1:FSQ3wMuphnPPGJOFhvc+cRQ2CT/rUj4cyQXkJcjOwz0= github.com/antchfx/xmlquery v1.3.18/go.mod h1:Afkq4JIeXut75taLSuI31ISJ/zeq+3jG7TunF7noreA= +github.com/antchfx/xmlquery v1.4.1 h1:YgpSwbeWvLp557YFTi8E3z6t6/hYjmFEtiEKbDfEbl0= +github.com/antchfx/xmlquery v1.4.1/go.mod h1:lKezcT8ELGt8kW5L+ckFMTbgdR61/odpPgDv8Gvi1fI= github.com/antchfx/xpath v1.2.1 h1:qhp4EW6aCOVr5XIkT+l6LJ9ck/JsUH/yyauNgTQkBF8= github.com/antchfx/xpath v1.2.1/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs= github.com/antchfx/xpath v1.2.3/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs= @@ -11,6 +13,8 @@ github.com/antchfx/xpath v1.2.4 h1:dW1HB/JxKvGtJ9WyVGJ0sIoEcqftV3SqIstujI+B9XY= github.com/antchfx/xpath v1.2.4/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs= github.com/antchfx/xpath v1.2.5 h1:hqZ+wtQ+KIOV/S3bGZcIhpgYC26um2bZYP2KVGcR7VY= github.com/antchfx/xpath v1.2.5/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs= +github.com/antchfx/xpath v1.3.1 h1:PNbFuUqHwWl0xRjvUPjJ95Agbmdj2uzzIwmQKgu4oCk= +github.com/antchfx/xpath v1.3.1/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -44,6 +48,8 @@ golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= +golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -73,6 +79,8 @@ golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= diff --git a/vendor/github.com/antchfx/xmlquery/.travis.yml b/vendor/github.com/antchfx/xmlquery/.travis.yml deleted file mode 100644 index 731b767..0000000 --- a/vendor/github.com/antchfx/xmlquery/.travis.yml +++ /dev/null @@ -1,17 +0,0 @@ -language: go - -go: - - 1.9.x - - 1.12.x - - 1.13.x - - 1.14.x - - 1.15.x - -install: - - go get golang.org/x/net/html/charset - - go get github.com/antchfx/xpath - - go get github.com/mattn/goveralls - - go get github.com/golang/groupcache - -script: - - $HOME/gopath/bin/goveralls -service=travis-ci diff --git a/vendor/github.com/antchfx/xmlquery/README.md b/vendor/github.com/antchfx/xmlquery/README.md index 11fafde..4d26bfe 100644 --- a/vendor/github.com/antchfx/xmlquery/README.md +++ b/vendor/github.com/antchfx/xmlquery/README.md @@ -1,37 +1,33 @@ -xmlquery -==== -[![Build Status](https://travis-ci.org/antchfx/xmlquery.svg?branch=master)](https://travis-ci.org/antchfx/xmlquery) -[![Coverage Status](https://coveralls.io/repos/github/antchfx/xmlquery/badge.svg?branch=master)](https://coveralls.io/github/antchfx/xmlquery?branch=master) +# xmlquery + +[![Build Status](https://github.com/antchfx/xmlquery/actions/workflows/testing.yml/badge.svg)](https://github.com/antchfx/xmlquery/actions/workflows/testing.yml) [![GoDoc](https://godoc.org/github.com/antchfx/xmlquery?status.svg)](https://godoc.org/github.com/antchfx/xmlquery) [![Go Report Card](https://goreportcard.com/badge/github.com/antchfx/xmlquery)](https://goreportcard.com/report/github.com/antchfx/xmlquery) -Overview -=== +# Overview -`xmlquery` is an XPath query package for XML documents, allowing you to extract +`xmlquery` is an XPath query package for XML documents, allowing you to extract data or evaluate from XML documents with an XPath expression. `xmlquery` has a built-in query object caching feature that caches recently used -XPATH query strings. Enabling caching can avoid recompile XPath expression for -each query. +XPATH query strings. Enabling caching can avoid recompile XPath expression for +each query. You can visit this page to learn about the supported XPath(1.0/2.0) syntax. https://github.com/antchfx/xpath -[htmlquery](https://github.com/antchfx/htmlquery) - Package for the HTML document query. +[htmlquery](https://github.com/antchfx/htmlquery) - Package for the HTML document query. -[xmlquery](https://github.com/antchfx/xmlquery) - Package for the XML document query. +[xmlquery](https://github.com/antchfx/xmlquery) - Package for the XML document query. -[jsonquery](https://github.com/antchfx/jsonquery) - Package for the JSON document query. +[jsonquery](https://github.com/antchfx/jsonquery) - Package for the JSON document query. + +# Installation -Installation -==== ``` $ go get github.com/antchfx/xmlquery ``` - -Quick Starts -=== +# Quick Starts ```go import ( @@ -75,8 +71,7 @@ func main(){ } ``` -Getting Started -=== +# Getting Started ### Find specified XPath query. @@ -157,10 +152,17 @@ list := xmlquery.Find(doc, "//author") book := xmlquery.FindOne(doc, "//book[2]") ``` -#### Find all book elements and only get `id` attribute. (New Feature) +#### Find the last book. + +```go +book := xmlquery.FindOne(doc, "//book[last()]") +``` + +#### Find all book elements and only get `id` attribute. ```go list := xmlquery.Find(doc,"//book/@id") +fmt.Println(list[0].InnerText) // outout @id value ``` #### Find all books with id `bk104`. @@ -183,15 +185,21 @@ price := expr.Evaluate(xmlquery.CreateXPathNavigator(doc)).(float64) fmt.Printf("total price: %f\n", price) ``` -#### Evaluate number of all book elements. +#### Count the number of books. ```go expr, err := xpath.Compile("count(//book)") +count := expr.Evaluate(xmlquery.CreateXPathNavigator(doc)).(float64) +``` + +#### Calculate the total price of all book prices. + +```go +expr, err := xpath.Compile("sum(//book/price)") price := expr.Evaluate(xmlquery.CreateXPathNavigator(doc)).(float64) ``` -Advanced Features -==== +# Advanced Features ### Parse `UTF-16` XML file with `ParseWithOptions()`. @@ -273,8 +281,7 @@ Output: W3Schools Home Page ``` -FAQ -==== +# FAQ #### `Find()` vs `QueryAll()`, which is better? @@ -284,12 +291,12 @@ an error. #### Can I save my query expression object for the next query? -Yes, you can. We provide `QuerySelector` and `QuerySelectorAll` methods; they +Yes, you can. We provide `QuerySelector` and `QuerySelectorAll` methods; they accept your query expression object. -Caching a query expression object avoids recompiling the XPath query +Caching a query expression object avoids recompiling the XPath query expression, improving query performance. -Questions -=== +# Questions + Please let me know if you have any questions diff --git a/vendor/github.com/antchfx/xmlquery/books.xml b/vendor/github.com/antchfx/xmlquery/books.xml deleted file mode 100644 index 85a74b5..0000000 --- a/vendor/github.com/antchfx/xmlquery/books.xml +++ /dev/null @@ -1,121 +0,0 @@ - - - - - Gambardella, Matthew - XML Developer's Guide - Computer - 44.95 - 2000-10-01 - An in-depth look at creating applications - with XML. - - - Ralls, Kim - Midnight Rain - Fantasy - 5.95 - 2000-12-16 - A former architect battles corporate zombies, - an evil sorceress, and her own childhood to become queen - of the world. - - - Corets, Eva - Maeve Ascendant - Fantasy - 5.95 - 2000-11-17 - After the collapse of a nanotechnology - society in England, the young survivors lay the - foundation for a new society. - - - Corets, Eva - Oberon's Legacy - Fantasy - 5.95 - 2001-03-10 - In post-apocalypse England, the mysterious - agent known only as Oberon helps to create a new life - for the inhabitants of London. Sequel to Maeve - Ascendant. - - - Corets, Eva - The Sundered Grail - Fantasy - 5.95 - 2001-09-10 - The two daughters of Maeve, half-sisters, - battle one another for control of England. Sequel to - Oberon's Legacy. - - - Randall, Cynthia - Lover Birds - Romance - 4.95 - 2000-09-02 - When Carla meets Paul at an ornithology - conference, tempers fly as feathers get ruffled. - - - Thurman, Paula - Splish Splash - Romance - 4.95 - 2000-11-02 - A deep sea diver finds true love twenty - thousand leagues beneath the sea. - - - Knorr, Stefan - Creepy Crawlies - Horror - 4.95 - 2000-12-06 - An anthology of horror stories about roaches, - centipedes, scorpions and other insects. - - - Kress, Peter - Paradox Lost - Science Fiction - 6.95 - 2000-11-02 - After an inadvertant trip through a Heisenberg - Uncertainty Device, James Salway discovers the problems - of being quantum. - - - O'Brien, Tim - Microsoft .NET: The Programming Bible - Computer - 36.95 - 2000-12-09 - Microsoft's .NET initiative is explored in - detail in this deep programmer's reference. - - - O'Brien, Tim - MSXML3: A Comprehensive Guide - Computer - 36.95 - 2000-12-01 - The Microsoft MSXML3 parser is covered in - detail, with attention to XML DOM interfaces, XSLT processing, - SAX and more. - - - Galos, Mike - Visual Studio 7: A Comprehensive Guide - Computer - 49.95 - 2001-04-16 - Microsoft Visual Studio 7 is explored in depth, - looking at how Visual Basic, Visual C++, C#, and ASP+ are - integrated into a comprehensive development - environment. - - \ No newline at end of file diff --git a/vendor/github.com/antchfx/xmlquery/node.go b/vendor/github.com/antchfx/xmlquery/node.go index 207892a..28f3f54 100644 --- a/vendor/github.com/antchfx/xmlquery/node.go +++ b/vendor/github.com/antchfx/xmlquery/node.go @@ -27,6 +27,8 @@ const ( CommentNode // AttributeNode is an attribute of element. AttributeNode + // NotationNode is a directive represents in document (for example, ). + NotationNode ) type Attr struct { @@ -99,6 +101,10 @@ func newXMLName(name string) xml.Name { } } +func (n *Node) Level() int { + return n.level +} + // InnerText returns the text between the start and end tags of the object. func (n *Node) InnerText() string { var output func(*strings.Builder, *Node) @@ -153,6 +159,9 @@ func outputXML(b *strings.Builder, n *Node, preserveSpaces bool, config *outputC b.WriteString("-->") } return + case NotationNode: + fmt.Fprintf(b, "", n.Data) + return case DeclarationNode: b.WriteString("= p.level) { + p.space2prefix[att.Value] = &xmlnsPrefix{name: "", level: p.level} // reset empty if exist the default namespace + } } else if att.Name.Space == "xmlns" { // maybe there are have duplicate NamespaceURL? - space2prefix[att.Value] = att.Name.Local + p.space2prefix[att.Value] = &xmlnsPrefix{name: att.Name.Local, level: p.level} } } if space := tok.Name.Space; space != "" { - if _, found := space2prefix[space]; !found && p.decoder.Strict { + if _, found := p.space2prefix[space]; !found && p.decoder.Strict { return nil, fmt.Errorf("xmlquery: invalid XML document, namespace %s is missing", space) } } @@ -125,8 +137,8 @@ func (p *parser) parse() (*Node, error) { attributes := make([]Attr, len(tok.Attr)) for i, att := range tok.Attr { name := att.Name - if prefix, ok := space2prefix[name.Space]; ok { - name.Space = prefix + if prefix, ok := p.space2prefix[name.Space]; ok { + name.Space = prefix.name } attributes[i] = Attr{ Name: name, @@ -155,10 +167,10 @@ func (p *parser) parse() (*Node, error) { } if node.NamespaceURI != "" { - if v, ok := space2prefix[node.NamespaceURI]; ok { + if v, ok := p.space2prefix[node.NamespaceURI]; ok { cached := string(p.reader.Cache()) - if strings.HasPrefix(cached, fmt.Sprintf("%s:%s", v, node.Data)) || strings.HasPrefix(cached, fmt.Sprintf("<%s:%s", v, node.Data)) { - node.Prefix = v + if strings.HasPrefix(cached, fmt.Sprintf("%s:%s", v.name, node.Data)) || strings.HasPrefix(cached, fmt.Sprintf("<%s:%s", v.name, node.Data)) { + node.Prefix = v.name } } } @@ -269,6 +281,17 @@ func (p *parser) parse() (*Node, error) { } p.prev = node case xml.Directive: + node := &Node{Type: NotationNode, Data: string(tok), level: p.level} + if p.level == p.prev.level { + AddSibling(p.prev, node) + } else if p.level > p.prev.level { + AddChild(p.prev, node) + } else if p.level < p.prev.level { + for i := p.prev.level - p.level; i > 1; i-- { + p.prev = p.prev.Parent + } + AddSibling(p.prev.Parent, node) + } } } } @@ -285,37 +308,43 @@ type StreamParser struct { // scenarios. // // Scenario 1: simple case: -// xml := `b1b2` -// sp, err := CreateStreamParser(strings.NewReader(xml), "/AAA/BBB") -// if err != nil { -// panic(err) -// } -// for { -// n, err := sp.Read() -// if err != nil { -// break -// } -// fmt.Println(n.OutputXML(true)) -// } +// +// xml := `b1b2` +// sp, err := CreateStreamParser(strings.NewReader(xml), "/AAA/BBB") +// if err != nil { +// panic(err) +// } +// for { +// n, err := sp.Read() +// if err != nil { +// break +// } +// fmt.Println(n.OutputXML(true)) +// } +// // Output will be: -// b1 -// b2 +// +// b1 +// b2 // // Scenario 2: advanced case: -// xml := `b1b2` -// sp, err := CreateStreamParser(strings.NewReader(xml), "/AAA/BBB", "/AAA/BBB[. != 'b1']") -// if err != nil { -// panic(err) -// } -// for { -// n, err := sp.Read() -// if err != nil { -// break -// } -// fmt.Println(n.OutputXML(true)) -// } +// +// xml := `b1b2` +// sp, err := CreateStreamParser(strings.NewReader(xml), "/AAA/BBB", "/AAA/BBB[. != 'b1']") +// if err != nil { +// panic(err) +// } +// for { +// n, err := sp.Read() +// if err != nil { +// break +// } +// fmt.Println(n.OutputXML(true)) +// } +// // Output will be: -// b2 +// +// b2 // // As the argument names indicate, streamElementXPath should be used for // providing xpath query pointing to the target element node only, no extra diff --git a/vendor/github.com/antchfx/xmlquery/query.go b/vendor/github.com/antchfx/xmlquery/query.go index 9f2493f..d1353aa 100644 --- a/vendor/github.com/antchfx/xmlquery/query.go +++ b/vendor/github.com/antchfx/xmlquery/query.go @@ -156,7 +156,7 @@ func (x *NodeNavigator) NodeType() xpath.NodeType { switch x.curr.Type { case CommentNode: return xpath.CommentNode - case TextNode, CharDataNode: + case TextNode, CharDataNode, NotationNode: return xpath.TextNode case DeclarationNode, DocumentNode: return xpath.RootNode diff --git a/vendor/github.com/antchfx/xpath/.travis.yml b/vendor/github.com/antchfx/xpath/.travis.yml deleted file mode 100644 index 6b63957..0000000 --- a/vendor/github.com/antchfx/xpath/.travis.yml +++ /dev/null @@ -1,12 +0,0 @@ -language: go - -go: - - 1.6 - - 1.9 - - '1.10' - -install: - - go get github.com/mattn/goveralls - -script: - - $HOME/gopath/bin/goveralls -service=travis-ci \ No newline at end of file diff --git a/vendor/github.com/antchfx/xpath/README.md b/vendor/github.com/antchfx/xpath/README.md index d1e3a3c..733c4c8 100644 --- a/vendor/github.com/antchfx/xpath/README.md +++ b/vendor/github.com/antchfx/xpath/README.md @@ -1,14 +1,13 @@ -XPath -==== +# XPath + [![GoDoc](https://godoc.org/github.com/antchfx/xpath?status.svg)](https://godoc.org/github.com/antchfx/xpath) [![Coverage Status](https://coveralls.io/repos/github/antchfx/xpath/badge.svg?branch=master)](https://coveralls.io/github/antchfx/xpath?branch=master) -[![Build Status](https://travis-ci.org/antchfx/xpath.svg?branch=master)](https://travis-ci.org/antchfx/xpath) +[![Build Status](https://github.com/antchfx/xpath/actions/workflows/testing.yml/badge.svg)](https://github.com/antchfx/xpath/actions/workflows/testing.yml) [![Go Report Card](https://goreportcard.com/badge/github.com/antchfx/xpath)](https://goreportcard.com/report/github.com/antchfx/xpath) XPath is Go package provides selecting nodes from XML, HTML or other documents using XPath expression. -Implementation -=== +# Implementation - [htmlquery](https://github.com/antchfx/htmlquery) - an XPath query package for HTML document @@ -16,8 +15,7 @@ Implementation - [jsonquery](https://github.com/antchfx/jsonquery) - an XPath query package for JSON document -Supported Features -=== +# Supported Features #### The basic XPath patterns. @@ -63,11 +61,14 @@ Supported Features - `child::*` : The child axis selects children of the current node. + - `child::node()`: Selects all the children of the context node. + - `child::text()`: Selects all text node children of the context node. + - `descendant::*` : The descendant axis selects descendants of the current node. It is equivalent to '//'. - `descendant-or-self::*` : Selects descendants including the current node. -- `attribute::*` : Selects attributes of the current element. It is equivalent to @* +- `attribute::*` : Selects attributes of the current element. It is equivalent to @\* - `following-sibling::*` : Selects nodes after the current node. @@ -87,27 +88,27 @@ Supported Features #### Expressions - The gxpath supported three types: number, boolean, string. +The gxpath supported three types: number, boolean, string. - `path` : Selects nodes based on the path. - `a = b` : Standard comparisons. - * a = b True if a equals b. - * a != b True if a is not equal to b. - * a < b True if a is less than b. - * a <= b True if a is less than or equal to b. - * a > b True if a is greater than b. - * a >= b True if a is greater than or equal to b. + - `a = b` : True if a equals b. + - `a != b` : True if a is not equal to b. + - `a < b` : True if a is less than b. + - `a <= b` : True if a is less than or equal to b. + - `a > b` : True if a is greater than b. + - `a >= b` : True if a is greater than or equal to b. - `a + b` : Arithmetic expressions. - * `- a` Unary minus - * a + b Add - * a - b Substract - * a * b Multiply - * a div b Divide - * a mod b Floating point mod, like Java. + - `- a` Unary minus + - `a + b` : Addition + - `a - b` : Subtraction + - `a * b` : Multiplication + - `a div b` : Division + - `a mod b` : Modulus (division remainder) - `a or b` : Boolean `or` operation. @@ -117,49 +118,50 @@ Supported Features - `fun(arg1, ..., argn)` : Function calls: -| Function | Supported | -| --- | --- | -`boolean()`| ✓ | -`ceiling()`| ✓ | -`choose()`| ✗ | -`concat()`| ✓ | -`contains()`| ✓ | -`count()`| ✓ | -`current()`| ✗ | -`document()`| ✗ | -`element-available()`| ✗ | -`ends-with()`| ✓ | -`false()`| ✓ | -`floor()`| ✓ | -`format-number()`| ✗ | -`function-available()`| ✗ | -`generate-id()`| ✗ | -`id()`| ✗ | -`key()`| ✗ | -`lang()`| ✗ | -`last()`| ✓ | -`local-name()`| ✓ | -`matches()`| ✓ | -`name()`| ✓ | -`namespace-uri()`| ✓ | -`normalize-space()`| ✓ | -`not()`| ✓ | -`number()`| ✓ | -`position()`| ✓ | -`replace()`| ✓ | -`reverse()`| ✓ | -`round()`| ✓ | -`starts-with()`| ✓ | -`string()`| ✓ | -`string-join()`[^1]| ✓ | -`string-length()`| ✓ | -`substring()`| ✓ | -`substring-after()`| ✓ | -`substring-before()`| ✓ | -`sum()`| ✓ | -`system-property()`| ✗ | -`translate()`| ✓ | -`true()`| ✓ | -`unparsed-entity-url()` | ✗ | +| Function | Supported | +| ----------------------- | --------- | +| `boolean()` | ✓ | +| `ceiling()` | ✓ | +| `choose()` | ✗ | +| `concat()` | ✓ | +| `contains()` | ✓ | +| `count()` | ✓ | +| `current()` | ✗ | +| `document()` | ✗ | +| `element-available()` | ✗ | +| `ends-with()` | ✓ | +| `false()` | ✓ | +| `floor()` | ✓ | +| `format-number()` | ✗ | +| `function-available()` | ✗ | +| `generate-id()` | ✗ | +| `id()` | ✗ | +| `key()` | ✗ | +| `lang()` | ✗ | +| `last()` | ✓ | +| `local-name()` | ✓ | +| `lower-case()`[^1] | ✓ | +| `matches()` | ✓ | +| `name()` | ✓ | +| `namespace-uri()` | ✓ | +| `normalize-space()` | ✓ | +| `not()` | ✓ | +| `number()` | ✓ | +| `position()` | ✓ | +| `replace()` | ✓ | +| `reverse()` | ✓ | +| `round()` | ✓ | +| `starts-with()` | ✓ | +| `string()` | ✓ | +| `string-join()`[^1] | ✓ | +| `string-length()` | ✓ | +| `substring()` | ✓ | +| `substring-after()` | ✓ | +| `substring-before()` | ✓ | +| `sum()` | ✓ | +| `system-property()` | ✗ | +| `translate()` | ✓ | +| `true()` | ✓ | +| `unparsed-entity-url()` | ✗ | -[^1]: XPath-2.0 expression \ No newline at end of file +[^1]: XPath-2.0 expression diff --git a/vendor/github.com/antchfx/xpath/build.go b/vendor/github.com/antchfx/xpath/build.go index 2977bbc..44f87d8 100644 --- a/vendor/github.com/antchfx/xpath/build.go +++ b/vendor/github.com/antchfx/xpath/build.go @@ -7,15 +7,39 @@ import ( type flag int -const ( - noneFlag flag = iota - filterFlag -) +var flagsEnum = struct { + None flag + SmartDesc flag + PosFilter flag + Filter flag + Condition flag +}{ + None: 0, + SmartDesc: 1, + PosFilter: 2, + Filter: 4, + Condition: 8, +} + +type builderProp int + +var builderProps = struct { + None builderProp + PosFilter builderProp + HasPosition builderProp + HasLast builderProp + NonFlat builderProp +}{ + None: 0, + PosFilter: 1, + HasPosition: 2, + HasLast: 4, + NonFlat: 8, +} // builder provides building an XPath expressions. type builder struct { - depth int - flag flag + parseDepth int firstInput query } @@ -63,23 +87,26 @@ func axisPredicate(root *axisNode) func(NodeNavigator) bool { return predicate } -// processAxisNode processes a query for the XPath axis node. -func (b *builder) processAxisNode(root *axisNode) (query, error) { +// processAxis processes a query for the XPath axis node. +func (b *builder) processAxis(root *axisNode, flags flag, props *builderProp) (query, error) { var ( - err error - qyInput query - qyOutput query - predicate = axisPredicate(root) + err error + qyInput query + qyOutput query ) + b.firstInput = nil + predicate := axisPredicate(root) if root.Input == nil { qyInput = &contextQuery{} + *props = builderProps.None } else { + inputFlags := flagsEnum.None if root.AxeType == "child" && (root.Input.Type() == nodeAxis) { if input := root.Input.(*axisNode); input.AxeType == "descendant-or-self" { var qyGrandInput query if input.Input != nil { - qyGrandInput, _ = b.processNode(input.Input) + qyGrandInput, _ = b.processNode(input.Input, flagsEnum.SmartDesc, props) } else { qyGrandInput = &contextQuery{} } @@ -94,14 +121,14 @@ func (b *builder) processAxisNode(root *axisNode) (query, error) { } return v } - // fix `//*[contains(@id,"food")]//*[contains(@id,"food")]`, see https://github.com/antchfx/htmlquery/issues/52 - // Skip the current node(Self:false) for the next descendants nodes. - _, ok := qyGrandInput.(*contextQuery) - qyOutput = &descendantQuery{Input: qyGrandInput, Predicate: filter, Self: ok} + qyOutput = &descendantQuery{name: root.LocalName, Input: qyGrandInput, Predicate: filter, Self: false} + *props |= builderProps.NonFlat return qyOutput, nil } + } else if ((flags & flagsEnum.Filter) == 0) && (root.AxeType == "descendant" || root.AxeType == "descendant-or-self") { + inputFlags |= flagsEnum.SmartDesc } - qyInput, err = b.processNode(root.Input) + qyInput, err = b.processNode(root.Input, inputFlags, props) if err != nil { return nil, err } @@ -109,11 +136,13 @@ func (b *builder) processAxisNode(root *axisNode) (query, error) { switch root.AxeType { case "ancestor": - qyOutput = &ancestorQuery{Input: qyInput, Predicate: predicate} + qyOutput = &ancestorQuery{name: root.LocalName, Input: qyInput, Predicate: predicate} + *props |= builderProps.NonFlat case "ancestor-or-self": - qyOutput = &ancestorQuery{Input: qyInput, Predicate: predicate, Self: true} + qyOutput = &ancestorQuery{name: root.LocalName, Input: qyInput, Predicate: predicate, Self: true} + *props |= builderProps.NonFlat case "attribute": - qyOutput = &attributeQuery{Input: qyInput, Predicate: predicate} + qyOutput = &attributeQuery{name: root.LocalName, Input: qyInput, Predicate: predicate} case "child": filter := func(n NodeNavigator) bool { v := predicate(n) @@ -127,19 +156,35 @@ func (b *builder) processAxisNode(root *axisNode) (query, error) { } return v } - qyOutput = &childQuery{Input: qyInput, Predicate: filter} + if (*props & builderProps.NonFlat) == 0 { + qyOutput = &childQuery{name: root.LocalName, Input: qyInput, Predicate: filter} + } else { + qyOutput = &cachedChildQuery{name: root.LocalName, Input: qyInput, Predicate: filter} + } case "descendant": - qyOutput = &descendantQuery{Input: qyInput, Predicate: predicate} + if (flags & flagsEnum.SmartDesc) != flagsEnum.None { + qyOutput = &descendantOverDescendantQuery{name: root.LocalName, Input: qyInput, MatchSelf: false, Predicate: predicate} + } else { + qyOutput = &descendantQuery{name: root.LocalName, Input: qyInput, Predicate: predicate} + } + *props |= builderProps.NonFlat case "descendant-or-self": - qyOutput = &descendantQuery{Input: qyInput, Predicate: predicate, Self: true} + if (flags & flagsEnum.SmartDesc) != flagsEnum.None { + qyOutput = &descendantOverDescendantQuery{name: root.LocalName, Input: qyInput, MatchSelf: true, Predicate: predicate} + } else { + qyOutput = &descendantQuery{name: root.LocalName, Input: qyInput, Predicate: predicate, Self: true} + } + *props |= builderProps.NonFlat case "following": qyOutput = &followingQuery{Input: qyInput, Predicate: predicate} + *props |= builderProps.NonFlat case "following-sibling": qyOutput = &followingQuery{Input: qyInput, Predicate: predicate, Sibling: true} case "parent": qyOutput = &parentQuery{Input: qyInput, Predicate: predicate} case "preceding": qyOutput = &precedingQuery{Input: qyInput, Predicate: predicate} + *props |= builderProps.NonFlat case "preceding-sibling": qyOutput = &precedingQuery{Input: qyInput, Predicate: predicate, Sibling: true} case "self": @@ -153,56 +198,182 @@ func (b *builder) processAxisNode(root *axisNode) (query, error) { return qyOutput, nil } -// processFilterNode builds query for the XPath filter predicate. -func (b *builder) processFilterNode(root *filterNode) (query, error) { - b.flag |= filterFlag +func canBeNumber(q query) bool { + if q.ValueType() != xpathResultType.Any { + return q.ValueType() == xpathResultType.Number + } + return true +} - qyInput, err := b.processNode(root.Input) +// processFilterNode builds query for the XPath filter predicate. +func (b *builder) processFilter(root *filterNode, flags flag, props *builderProp) (query, error) { + first := (flags & flagsEnum.Filter) == 0 + + qyInput, err := b.processNode(root.Input, (flags | flagsEnum.Filter), props) if err != nil { return nil, err } - qyCond, err := b.processNode(root.Condition) + firstInput := b.firstInput + + var propsCond builderProp + cond, err := b.processNode(root.Condition, flags, &propsCond) if err != nil { return nil, err } - qyOutput := &filterQuery{Input: qyInput, Predicate: qyCond} - return qyOutput, nil + + // Checking whether is number + if canBeNumber(cond) || ((propsCond & (builderProps.HasPosition | builderProps.HasLast)) != 0) { + propsCond |= builderProps.HasPosition + flags |= flagsEnum.PosFilter + } + + if root.Input.Type() != nodeFilter { + *props &= ^builderProps.PosFilter + } + + if (propsCond & builderProps.HasPosition) != 0 { + *props |= builderProps.PosFilter + } + + merge := (qyInput.Properties() & queryProps.Merge) != 0 + if (propsCond & builderProps.HasPosition) != builderProps.None { + if (propsCond & builderProps.HasLast) != 0 { + // https://github.com/antchfx/xpath/issues/76 + // https://github.com/antchfx/xpath/issues/78 + if qyFunc, ok := cond.(*functionQuery); ok { + switch qyFunc.Input.(type) { + case *filterQuery: + cond = &lastQuery{Input: qyFunc.Input} + } + } + } + } + + if first && firstInput != nil { + if merge && ((*props & builderProps.PosFilter) != 0) { + qyInput = &filterQuery{Input: qyInput, Predicate: cond, NoPosition: false} + + var ( + rootQuery = &contextQuery{} + parent query + ) + switch axisQuery := firstInput.(type) { + case *ancestorQuery: + if _, ok := axisQuery.Input.(*contextQuery); !ok { + parent = axisQuery.Input + axisQuery.Input = rootQuery + } + case *attributeQuery: + if _, ok := axisQuery.Input.(*contextQuery); !ok { + parent = axisQuery.Input + axisQuery.Input = rootQuery + } + case *childQuery: + if _, ok := axisQuery.Input.(*contextQuery); !ok { + parent = axisQuery.Input + axisQuery.Input = rootQuery + } + case *cachedChildQuery: + if _, ok := axisQuery.Input.(*contextQuery); !ok { + parent = axisQuery.Input + axisQuery.Input = rootQuery + } + case *descendantQuery: + if _, ok := axisQuery.Input.(*contextQuery); !ok { + parent = axisQuery.Input + axisQuery.Input = rootQuery + } + case *followingQuery: + if _, ok := axisQuery.Input.(*contextQuery); !ok { + parent = axisQuery.Input + axisQuery.Input = rootQuery + } + case *precedingQuery: + if _, ok := axisQuery.Input.(*contextQuery); !ok { + parent = axisQuery.Input + axisQuery.Input = rootQuery + } + case *parentQuery: + if _, ok := axisQuery.Input.(*contextQuery); !ok { + parent = axisQuery.Input + axisQuery.Input = rootQuery + } + case *selfQuery: + if _, ok := axisQuery.Input.(*contextQuery); !ok { + parent = axisQuery.Input + axisQuery.Input = rootQuery + } + case *groupQuery: + if _, ok := axisQuery.Input.(*contextQuery); !ok { + parent = axisQuery.Input + axisQuery.Input = rootQuery + } + case *descendantOverDescendantQuery: + if _, ok := axisQuery.Input.(*contextQuery); !ok { + parent = axisQuery.Input + axisQuery.Input = rootQuery + } + } + b.firstInput = nil + if parent != nil { + return &mergeQuery{Input: parent, Child: qyInput}, nil + } + return qyInput, nil + } + b.firstInput = nil + } + + resultQuery := &filterQuery{ + Input: qyInput, + Predicate: cond, + NoPosition: (propsCond & builderProps.HasPosition) == 0, + } + return resultQuery, nil } // processFunctionNode processes query for the XPath function node. -func (b *builder) processFunctionNode(root *functionNode) (query, error) { +func (b *builder) processFunction(root *functionNode, props *builderProp) (query, error) { + // Reset builder props + *props = builderProps.None + var qyOutput query switch root.FuncName { + case "lower-case": + arg, err := b.processNode(root.Args[0], flagsEnum.None, props) + if err != nil { + return nil, err + } + qyOutput = &functionQuery{Input: arg, Func: lowerCaseFunc} case "starts-with": - arg1, err := b.processNode(root.Args[0]) + arg1, err := b.processNode(root.Args[0], flagsEnum.None, props) if err != nil { return nil, err } - arg2, err := b.processNode(root.Args[1]) + arg2, err := b.processNode(root.Args[1], flagsEnum.None, props) if err != nil { return nil, err } - qyOutput = &functionQuery{Input: b.firstInput, Func: startwithFunc(arg1, arg2)} + qyOutput = &functionQuery{Func: startwithFunc(arg1, arg2)} case "ends-with": - arg1, err := b.processNode(root.Args[0]) + arg1, err := b.processNode(root.Args[0], flagsEnum.None, props) if err != nil { return nil, err } - arg2, err := b.processNode(root.Args[1]) + arg2, err := b.processNode(root.Args[1], flagsEnum.None, props) if err != nil { return nil, err } - qyOutput = &functionQuery{Input: b.firstInput, Func: endwithFunc(arg1, arg2)} + qyOutput = &functionQuery{Func: endwithFunc(arg1, arg2)} case "contains": - arg1, err := b.processNode(root.Args[0]) + arg1, err := b.processNode(root.Args[0], flagsEnum.None, props) if err != nil { return nil, err } - arg2, err := b.processNode(root.Args[1]) + arg2, err := b.processNode(root.Args[1], flagsEnum.None, props) if err != nil { return nil, err } - qyOutput = &functionQuery{Input: b.firstInput, Func: containsFunc(arg1, arg2)} + qyOutput = &functionQuery{Func: containsFunc(arg1, arg2)} case "matches": //matches(string , pattern) if len(root.Args) != 2 { @@ -212,10 +383,10 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) { arg1, arg2 query err error ) - if arg1, err = b.processNode(root.Args[0]); err != nil { + if arg1, err = b.processNode(root.Args[0], flagsEnum.None, props); err != nil { return nil, err } - if arg2, err = b.processNode(root.Args[1]); err != nil { + if arg2, err = b.processNode(root.Args[1], flagsEnum.None, props); err != nil { return nil, err } // Issue #92, testing the regular expression before. @@ -224,7 +395,7 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) { return nil, fmt.Errorf("matches() got error. %v", err) } } - qyOutput = &functionQuery{Input: b.firstInput, Func: matchesFunc(arg1, arg2)} + qyOutput = &functionQuery{Func: matchesFunc(arg1, arg2)} case "substring": //substring( string , start [, length] ) if len(root.Args) < 2 { @@ -234,18 +405,18 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) { arg1, arg2, arg3 query err error ) - if arg1, err = b.processNode(root.Args[0]); err != nil { + if arg1, err = b.processNode(root.Args[0], flagsEnum.None, props); err != nil { return nil, err } - if arg2, err = b.processNode(root.Args[1]); err != nil { + if arg2, err = b.processNode(root.Args[1], flagsEnum.None, props); err != nil { return nil, err } if len(root.Args) == 3 { - if arg3, err = b.processNode(root.Args[2]); err != nil { + if arg3, err = b.processNode(root.Args[2], flagsEnum.None, props); err != nil { return nil, err } } - qyOutput = &functionQuery{Input: b.firstInput, Func: substringFunc(arg1, arg2, arg3)} + qyOutput = &functionQuery{Func: substringFunc(arg1, arg2, arg3)} case "substring-before", "substring-after": //substring-xxxx( haystack, needle ) if len(root.Args) != 2 { @@ -255,31 +426,30 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) { arg1, arg2 query err error ) - if arg1, err = b.processNode(root.Args[0]); err != nil { + if arg1, err = b.processNode(root.Args[0], flagsEnum.None, props); err != nil { return nil, err } - if arg2, err = b.processNode(root.Args[1]); err != nil { + if arg2, err = b.processNode(root.Args[1], flagsEnum.None, props); err != nil { return nil, err } qyOutput = &functionQuery{ - Input: b.firstInput, - Func: substringIndFunc(arg1, arg2, root.FuncName == "substring-after"), + Func: substringIndFunc(arg1, arg2, root.FuncName == "substring-after"), } case "string-length": // string-length( [string] ) if len(root.Args) < 1 { return nil, errors.New("xpath: string-length function must have at least one parameter") } - arg1, err := b.processNode(root.Args[0]) + arg1, err := b.processNode(root.Args[0], flagsEnum.None, props) if err != nil { return nil, err } - qyOutput = &functionQuery{Input: b.firstInput, Func: stringLengthFunc(arg1)} + qyOutput = &functionQuery{Func: stringLengthFunc(arg1)} case "normalize-space": if len(root.Args) == 0 { return nil, errors.New("xpath: normalize-space function must have at least one parameter") } - argQuery, err := b.processNode(root.Args[0]) + argQuery, err := b.processNode(root.Args[0], flagsEnum.None, props) if err != nil { return nil, err } @@ -293,16 +463,16 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) { arg1, arg2, arg3 query err error ) - if arg1, err = b.processNode(root.Args[0]); err != nil { + if arg1, err = b.processNode(root.Args[0], flagsEnum.None, props); err != nil { return nil, err } - if arg2, err = b.processNode(root.Args[1]); err != nil { + if arg2, err = b.processNode(root.Args[1], flagsEnum.None, props); err != nil { return nil, err } - if arg3, err = b.processNode(root.Args[2]); err != nil { + if arg3, err = b.processNode(root.Args[2], flagsEnum.None, props); err != nil { return nil, err } - qyOutput = &functionQuery{Input: b.firstInput, Func: replaceFunc(arg1, arg2, arg3)} + qyOutput = &functionQuery{Func: replaceFunc(arg1, arg2, arg3)} case "translate": //translate( string , string, string ) if len(root.Args) != 3 { @@ -312,21 +482,21 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) { arg1, arg2, arg3 query err error ) - if arg1, err = b.processNode(root.Args[0]); err != nil { + if arg1, err = b.processNode(root.Args[0], flagsEnum.None, props); err != nil { return nil, err } - if arg2, err = b.processNode(root.Args[1]); err != nil { + if arg2, err = b.processNode(root.Args[1], flagsEnum.None, props); err != nil { return nil, err } - if arg3, err = b.processNode(root.Args[2]); err != nil { + if arg3, err = b.processNode(root.Args[2], flagsEnum.None, props); err != nil { return nil, err } - qyOutput = &functionQuery{Input: b.firstInput, Func: translateFunc(arg1, arg2, arg3)} + qyOutput = &functionQuery{Func: translateFunc(arg1, arg2, arg3)} case "not": if len(root.Args) == 0 { return nil, errors.New("xpath: not function must have at least one parameter") } - argQuery, err := b.processNode(root.Args[0]) + argQuery, err := b.processNode(root.Args[0], flagsEnum.None, props) if err != nil { return nil, err } @@ -340,46 +510,46 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) { err error ) if len(root.Args) == 1 { - arg, err = b.processNode(root.Args[0]) + arg, err = b.processNode(root.Args[0], flagsEnum.None, props) if err != nil { return nil, err } } switch root.FuncName { case "name": - qyOutput = &functionQuery{Input: b.firstInput, Func: nameFunc(arg)} + qyOutput = &functionQuery{Func: nameFunc(arg)} case "local-name": - qyOutput = &functionQuery{Input: b.firstInput, Func: localNameFunc(arg)} + qyOutput = &functionQuery{Func: localNameFunc(arg)} case "namespace-uri": - qyOutput = &functionQuery{Input: b.firstInput, Func: namespaceFunc(arg)} + qyOutput = &functionQuery{Func: namespaceFunc(arg)} } case "true", "false": val := root.FuncName == "true" qyOutput = &functionQuery{ - Input: b.firstInput, Func: func(_ query, _ iterator) interface{} { return val }, } case "last": - switch typ := b.firstInput.(type) { - case *groupQuery, *filterQuery: - // https://github.com/antchfx/xpath/issues/76 - // https://github.com/antchfx/xpath/issues/78 - qyOutput = &lastQuery{Input: typ} - default: - qyOutput = &functionQuery{Input: b.firstInput, Func: lastFunc} - } - + //switch typ := b.firstInput.(type) { + //case *groupQuery, *filterQuery: + // https://github.com/antchfx/xpath/issues/76 + // https://github.com/antchfx/xpath/issues/78 + //qyOutput = &lastQuery{Input: typ} + //default: + qyOutput = &functionQuery{Func: lastFunc} + //} + *props |= builderProps.HasLast case "position": - qyOutput = &functionQuery{Input: b.firstInput, Func: positionFunc} + qyOutput = &functionQuery{Func: positionFunc} + *props |= builderProps.HasPosition case "boolean", "number", "string": - inp := b.firstInput + var inp query if len(root.Args) > 1 { return nil, fmt.Errorf("xpath: %s function must have at most one parameter", root.FuncName) } if len(root.Args) == 1 { - argQuery, err := b.processNode(root.Args[0]) + argQuery, err := b.processNode(root.Args[0], flagsEnum.None, props) if err != nil { return nil, err } @@ -396,13 +566,10 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) { } qyOutput = f case "count": - //if b.firstInput == nil { - // return nil, errors.New("xpath: expression must evaluate to node-set") - //} if len(root.Args) == 0 { return nil, fmt.Errorf("xpath: count(node-sets) function must with have parameters node-sets") } - argQuery, err := b.processNode(root.Args[0]) + argQuery, err := b.processNode(root.Args[0], flagsEnum.None, props) if err != nil { return nil, err } @@ -411,7 +578,7 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) { if len(root.Args) == 0 { return nil, fmt.Errorf("xpath: sum(node-sets) function must with have parameters node-sets") } - argQuery, err := b.processNode(root.Args[0]) + argQuery, err := b.processNode(root.Args[0], flagsEnum.None, props) if err != nil { return nil, err } @@ -420,7 +587,7 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) { if len(root.Args) == 0 { return nil, fmt.Errorf("xpath: ceiling(node-sets) function must with have parameters node-sets") } - argQuery, err := b.processNode(root.Args[0]) + argQuery, err := b.processNode(root.Args[0], flagsEnum.None, props) if err != nil { return nil, err } @@ -440,18 +607,18 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) { } var args []query for _, v := range root.Args { - q, err := b.processNode(v) + q, err := b.processNode(v, flagsEnum.None, props) if err != nil { return nil, err } args = append(args, q) } - qyOutput = &functionQuery{Input: b.firstInput, Func: concatFunc(args...)} + qyOutput = &functionQuery{Func: concatFunc(args...)} case "reverse": if len(root.Args) == 0 { return nil, fmt.Errorf("xpath: reverse(node-sets) function must with have parameters node-sets") } - argQuery, err := b.processNode(root.Args[0]) + argQuery, err := b.processNode(root.Args[0], flagsEnum.None, props) if err != nil { return nil, err } @@ -460,11 +627,11 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) { if len(root.Args) != 2 { return nil, fmt.Errorf("xpath: string-join(node-sets, separator) function requires node-set and argument") } - argQuery, err := b.processNode(root.Args[0]) + argQuery, err := b.processNode(root.Args[0], flagsEnum.None, props) if err != nil { return nil, err } - arg1, err := b.processNode(root.Args[1]) + arg1, err := b.processNode(root.Args[1], flagsEnum.None, props) if err != nil { return nil, err } @@ -472,22 +639,33 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) { default: return nil, fmt.Errorf("not yet support this function %s()", root.FuncName) } + + if funcQuery, ok := qyOutput.(*functionQuery); ok && funcQuery.Input == nil { + funcQuery.Input = b.firstInput + } return qyOutput, nil } -func (b *builder) processOperatorNode(root *operatorNode) (query, error) { - left, err := b.processNode(root.Left) +func (b *builder) processOperator(root *operatorNode, props *builderProp) (query, error) { + var ( + leftProp builderProp + rightProp builderProp + ) + + left, err := b.processNode(root.Left, flagsEnum.None, &leftProp) if err != nil { return nil, err } - right, err := b.processNode(root.Right) + right, err := b.processNode(root.Right, flagsEnum.None, &rightProp) if err != nil { return nil, err } + *props = leftProp | rightProp + var qyOutput query switch root.Op { case "+", "-", "*", "div", "mod": // Numeric operator - var exprFunc func(interface{}, interface{}) interface{} + var exprFunc func(iterator, interface{}, interface{}) interface{} switch root.Op { case "+": exprFunc = plusFunc @@ -525,41 +703,45 @@ func (b *builder) processOperatorNode(root *operatorNode) (query, error) { } qyOutput = &booleanQuery{Left: left, Right: right, IsOr: isOr} case "|": + *props |= builderProps.NonFlat qyOutput = &unionQuery{Left: left, Right: right} } return qyOutput, nil } -func (b *builder) processNode(root node) (q query, err error) { - if b.depth = b.depth + 1; b.depth > 1024 { +func (b *builder) processNode(root node, flags flag, props *builderProp) (q query, err error) { + if b.parseDepth = b.parseDepth + 1; b.parseDepth > 1024 { err = errors.New("the xpath expressions is too complex") return } - + *props = builderProps.None switch root.Type() { case nodeConstantOperand: n := root.(*operandNode) q = &constantQuery{Val: n.Val} case nodeRoot: - q = &contextQuery{Root: true} + q = &absoluteQuery{} case nodeAxis: - q, err = b.processAxisNode(root.(*axisNode)) + q, err = b.processAxis(root.(*axisNode), flags, props) b.firstInput = q case nodeFilter: - q, err = b.processFilterNode(root.(*filterNode)) + q, err = b.processFilter(root.(*filterNode), flags, props) b.firstInput = q case nodeFunction: - q, err = b.processFunctionNode(root.(*functionNode)) + q, err = b.processFunction(root.(*functionNode), props) case nodeOperator: - q, err = b.processOperatorNode(root.(*operatorNode)) + q, err = b.processOperator(root.(*operatorNode), props) case nodeGroup: - q, err = b.processNode(root.(*groupNode).Input) + q, err = b.processNode(root.(*groupNode).Input, flagsEnum.None, props) if err != nil { return } q = &groupQuery{Input: q} - b.firstInput = q + if b.firstInput == nil { + b.firstInput = q + } } + b.parseDepth-- return } @@ -579,5 +761,6 @@ func build(expr string, namespaces map[string]string) (q query, err error) { }() root := parse(expr, namespaces) b := &builder{} - return b.processNode(root) + props := builderProps.None + return b.processNode(root, flagsEnum.None, &props) } diff --git a/vendor/github.com/antchfx/xpath/func.go b/vendor/github.com/antchfx/xpath/func.go index 4131bfd..a5e88ba 100644 --- a/vendor/github.com/antchfx/xpath/func.go +++ b/vendor/github.com/antchfx/xpath/func.go @@ -113,7 +113,7 @@ func asNumber(t iterator, o interface{}) float64 { case query: node := typ.Select(t) if node == nil { - return float64(0) + return math.NaN() } if v, err := strconv.ParseFloat(node.Value(), 64); err == nil { return v @@ -645,3 +645,9 @@ func stringJoinFunc(arg1 query) func(query, iterator) interface{} { return strings.Join(parts, separator) } } + +// lower-case is XPATH function that converts a string to lower case. +func lowerCaseFunc(q query, t iterator) interface{} { + v := functionArgs(q).Evaluate(t) + return strings.ToLower(asString(t, v)) +} diff --git a/vendor/github.com/antchfx/xpath/operator.go b/vendor/github.com/antchfx/xpath/operator.go index eb38ac6..2820152 100644 --- a/vendor/github.com/antchfx/xpath/operator.go +++ b/vendor/github.com/antchfx/xpath/operator.go @@ -1,40 +1,11 @@ package xpath import ( - "fmt" - "reflect" "strconv" ) // The XPath number operator function list. -// valueType is a return value type. -type valueType int - -const ( - booleanType valueType = iota - numberType - stringType - nodeSetType -) - -func getValueType(i interface{}) valueType { - v := reflect.ValueOf(i) - switch v.Kind() { - case reflect.Float64: - return numberType - case reflect.String: - return stringType - case reflect.Bool: - return booleanType - default: - if _, ok := i.(query); ok { - return nodeSetType - } - } - panic(fmt.Errorf("xpath unknown value type: %v", v.Kind())) -} - type logical func(iterator, string, interface{}, interface{}) bool var logicalFuncs = [][]logical{ @@ -228,91 +199,90 @@ func cmpBooleanBoolean(t iterator, op string, m, n interface{}) bool { // eqFunc is an `=` operator. func eqFunc(t iterator, m, n interface{}) interface{} { - t1 := getValueType(m) - t2 := getValueType(n) + t1 := getXPathType(m) + t2 := getXPathType(n) return logicalFuncs[t1][t2](t, "=", m, n) } // gtFunc is an `>` operator. func gtFunc(t iterator, m, n interface{}) interface{} { - t1 := getValueType(m) - t2 := getValueType(n) + t1 := getXPathType(m) + t2 := getXPathType(n) return logicalFuncs[t1][t2](t, ">", m, n) } // geFunc is an `>=` operator. func geFunc(t iterator, m, n interface{}) interface{} { - t1 := getValueType(m) - t2 := getValueType(n) + t1 := getXPathType(m) + t2 := getXPathType(n) return logicalFuncs[t1][t2](t, ">=", m, n) } // ltFunc is an `<` operator. func ltFunc(t iterator, m, n interface{}) interface{} { - t1 := getValueType(m) - t2 := getValueType(n) + t1 := getXPathType(m) + t2 := getXPathType(n) return logicalFuncs[t1][t2](t, "<", m, n) } // leFunc is an `<=` operator. func leFunc(t iterator, m, n interface{}) interface{} { - t1 := getValueType(m) - t2 := getValueType(n) + t1 := getXPathType(m) + t2 := getXPathType(n) return logicalFuncs[t1][t2](t, "<=", m, n) } // neFunc is an `!=` operator. func neFunc(t iterator, m, n interface{}) interface{} { - t1 := getValueType(m) - t2 := getValueType(n) + t1 := getXPathType(m) + t2 := getXPathType(n) return logicalFuncs[t1][t2](t, "!=", m, n) } // orFunc is an `or` operator. var orFunc = func(t iterator, m, n interface{}) interface{} { - t1 := getValueType(m) - t2 := getValueType(n) + t1 := getXPathType(m) + t2 := getXPathType(n) return logicalFuncs[t1][t2](t, "or", m, n) } -func numericExpr(m, n interface{}, cb func(float64, float64) float64) float64 { - typ := reflect.TypeOf(float64(0)) - a := reflect.ValueOf(m).Convert(typ) - b := reflect.ValueOf(n).Convert(typ) - return cb(a.Float(), b.Float()) +func numericExpr(t iterator, m, n interface{}, cb func(float64, float64) float64) float64 { + a := asNumber(t, m) + b := asNumber(t, n) + return cb(a, b) } // plusFunc is an `+` operator. -var plusFunc = func(m, n interface{}) interface{} { - return numericExpr(m, n, func(a, b float64) float64 { +var plusFunc = func(t iterator, m, n interface{}) interface{} { + return numericExpr(t, m, n, func(a, b float64) float64 { return a + b }) } // minusFunc is an `-` operator. -var minusFunc = func(m, n interface{}) interface{} { - return numericExpr(m, n, func(a, b float64) float64 { +var minusFunc = func(t iterator, m, n interface{}) interface{} { + return numericExpr(t, m, n, func(a, b float64) float64 { return a - b }) } // mulFunc is an `*` operator. -var mulFunc = func(m, n interface{}) interface{} { - return numericExpr(m, n, func(a, b float64) float64 { +var mulFunc = func(t iterator, m, n interface{}) interface{} { + return numericExpr(t, m, n, func(a, b float64) float64 { return a * b }) } // divFunc is an `DIV` operator. -var divFunc = func(m, n interface{}) interface{} { - return numericExpr(m, n, func(a, b float64) float64 { +var divFunc = func(t iterator, m, n interface{}) interface{} { + return numericExpr(t, m, n, func(a, b float64) float64 { return a / b }) } // modFunc is an 'MOD' operator. -var modFunc = func(m, n interface{}) interface{} { - return numericExpr(m, n, func(a, b float64) float64 { +var modFunc = func(t iterator, m, n interface{}) interface{} { + return numericExpr(t, m, n, func(a, b float64) float64 { return float64(int(a) % int(b)) }) } diff --git a/vendor/github.com/antchfx/xpath/query.go b/vendor/github.com/antchfx/xpath/query.go index 4e6c634..a4d1dce 100644 --- a/vendor/github.com/antchfx/xpath/query.go +++ b/vendor/github.com/antchfx/xpath/query.go @@ -7,6 +7,44 @@ import ( "reflect" ) +// The return type of the XPath expression. +type resultType int + +var xpathResultType = struct { + Boolean resultType + // A numeric value + Number resultType + String resultType + // A node collection. + NodeSet resultType + // Any of the XPath node types. + Any resultType +}{ + Boolean: 0, + Number: 1, + String: 2, + NodeSet: 3, + Any: 4, +} + +type queryProp int + +var queryProps = struct { + None queryProp + Position queryProp + Count queryProp + Cached queryProp + Reverse queryProp + Merge queryProp +}{ + None: 0, + Position: 1, + Count: 2, + Cached: 4, + Reverse: 8, + Merge: 16, +} + type iterator interface { Current() NodeNavigator } @@ -20,12 +58,15 @@ type query interface { Evaluate(iterator) interface{} Clone() query + + // ValueType returns the value type of the current query. + ValueType() resultType + + Properties() queryProp } // nopQuery is an empty query that always return nil for any query. -type nopQuery struct { - query -} +type nopQuery struct{} func (nopQuery) Select(iterator) NodeNavigator { return nil } @@ -33,21 +74,23 @@ func (nopQuery) Evaluate(iterator) interface{} { return nil } func (nopQuery) Clone() query { return nopQuery{} } +func (nopQuery) ValueType() resultType { return xpathResultType.NodeSet } + +func (nopQuery) Properties() queryProp { + return queryProps.Merge | queryProps.Position | queryProps.Count | queryProps.Cached +} + // contextQuery is returns current node on the iterator object query. type contextQuery struct { count int - Root bool // Moving to root-level node in the current context iterator. } -func (c *contextQuery) Select(t iterator) (n NodeNavigator) { - if c.count == 0 { - c.count++ - n = t.Current().Copy() - if c.Root { - n.MoveToRoot() - } +func (c *contextQuery) Select(t iterator) NodeNavigator { + if c.count > 0 { + return nil } - return n + c.count++ + return t.Current().Copy() } func (c *contextQuery) Evaluate(iterator) interface{} { @@ -56,12 +99,53 @@ func (c *contextQuery) Evaluate(iterator) interface{} { } func (c *contextQuery) Clone() query { - return &contextQuery{Root: c.Root} + return &contextQuery{} +} + +func (c *contextQuery) ValueType() resultType { + return xpathResultType.NodeSet +} + +func (c *contextQuery) Properties() queryProp { + return queryProps.Merge | queryProps.Position | queryProps.Count | queryProps.Cached +} + +type absoluteQuery struct { + count int +} + +func (a *absoluteQuery) Select(t iterator) (n NodeNavigator) { + if a.count > 0 { + return + } + a.count++ + n = t.Current().Copy() + n.MoveToRoot() + return +} + +func (a *absoluteQuery) Evaluate(t iterator) interface{} { + a.count = 0 + return a +} + +func (a *absoluteQuery) Clone() query { + return &absoluteQuery{} +} + +func (a *absoluteQuery) ValueType() resultType { + return xpathResultType.NodeSet +} + +func (a *absoluteQuery) Properties() queryProp { + return queryProps.Merge | queryProps.Position | queryProps.Count | queryProps.Cached } // ancestorQuery is an XPath ancestor node query.(ancestor::*|ancestor-self::*) type ancestorQuery struct { + name string iterator func() NodeNavigator + table map[uint64]bool Self bool Input query @@ -69,6 +153,10 @@ type ancestorQuery struct { } func (a *ancestorQuery) Select(t iterator) NodeNavigator { + if a.table == nil { + a.table = make(map[uint64]bool) + } + for { if a.iterator == nil { node := a.Input.Select(t) @@ -78,24 +166,27 @@ func (a *ancestorQuery) Select(t iterator) NodeNavigator { first := true node = node.Copy() a.iterator = func() NodeNavigator { - if first && a.Self { + if first { first = false - if a.Predicate(node) { + if a.Self && a.Predicate(node) { return node } } for node.MoveToParent() { - if !a.Predicate(node) { - continue + if a.Predicate(node) { + return node } - return node } return nil } } - if node := a.iterator(); node != nil { - return node + for node := a.iterator(); node != nil; node = a.iterator() { + node_id := getHashCode(node.Copy()) + if _, ok := a.table[node_id]; !ok { + a.table[node_id] = true + return node + } } a.iterator = nil } @@ -112,11 +203,20 @@ func (a *ancestorQuery) Test(n NodeNavigator) bool { } func (a *ancestorQuery) Clone() query { - return &ancestorQuery{Self: a.Self, Input: a.Input.Clone(), Predicate: a.Predicate} + return &ancestorQuery{name: a.name, Self: a.Self, Input: a.Input.Clone(), Predicate: a.Predicate} +} + +func (a *ancestorQuery) ValueType() resultType { + return xpathResultType.NodeSet +} + +func (a *ancestorQuery) Properties() queryProp { + return queryProps.Position | queryProps.Count | queryProps.Cached | queryProps.Merge | queryProps.Reverse } // attributeQuery is an XPath attribute node query.(@*) type attributeQuery struct { + name string iterator func() NodeNavigator Input query @@ -162,11 +262,20 @@ func (a *attributeQuery) Test(n NodeNavigator) bool { } func (a *attributeQuery) Clone() query { - return &attributeQuery{Input: a.Input.Clone(), Predicate: a.Predicate} + return &attributeQuery{name: a.name, Input: a.Input.Clone(), Predicate: a.Predicate} +} + +func (a *attributeQuery) ValueType() resultType { + return xpathResultType.NodeSet +} + +func (a *attributeQuery) Properties() queryProp { + return queryProps.Merge } // childQuery is an XPath child node query.(child::*) type childQuery struct { + name string posit int iterator func() NodeNavigator @@ -216,7 +325,15 @@ func (c *childQuery) Test(n NodeNavigator) bool { } func (c *childQuery) Clone() query { - return &childQuery{Input: c.Input.Clone(), Predicate: c.Predicate} + return &childQuery{name: c.name, Input: c.Input.Clone(), Predicate: c.Predicate} +} + +func (c *childQuery) ValueType() resultType { + return xpathResultType.NodeSet +} + +func (c *childQuery) Properties() queryProp { + return queryProps.Merge } // position returns a position of current NodeNavigator. @@ -224,8 +341,75 @@ func (c *childQuery) position() int { return c.posit } +type cachedChildQuery struct { + name string + posit int + iterator func() NodeNavigator + + Input query + Predicate func(NodeNavigator) bool +} + +func (c *cachedChildQuery) Select(t iterator) NodeNavigator { + for { + if c.iterator == nil { + c.posit = 0 + node := c.Input.Select(t) + if node == nil { + return nil + } + node = node.Copy() + first := true + c.iterator = func() NodeNavigator { + for { + if (first && !node.MoveToChild()) || (!first && !node.MoveToNext()) { + return nil + } + first = false + if c.Predicate(node) { + return node + } + } + } + } + + if node := c.iterator(); node != nil { + c.posit++ + return node + } + c.iterator = nil + } +} + +func (c *cachedChildQuery) Evaluate(t iterator) interface{} { + c.Input.Evaluate(t) + c.iterator = nil + return c +} + +func (c *cachedChildQuery) position() int { + return c.posit +} + +func (c *cachedChildQuery) Test(n NodeNavigator) bool { + return c.Predicate(n) +} + +func (c *cachedChildQuery) Clone() query { + return &childQuery{name: c.name, Input: c.Input.Clone(), Predicate: c.Predicate} +} + +func (c *cachedChildQuery) ValueType() resultType { + return xpathResultType.NodeSet +} + +func (c *cachedChildQuery) Properties() queryProp { + return queryProps.Merge +} + // descendantQuery is an XPath descendant node query.(descendant::* | descendant-or-self::*) type descendantQuery struct { + name string iterator func() NodeNavigator posit int level int @@ -245,14 +429,11 @@ func (d *descendantQuery) Select(t iterator) NodeNavigator { } node = node.Copy() d.level = 0 - positmap := make(map[int]int) first := true d.iterator = func() NodeNavigator { - if first && d.Self { + if first { first = false - if d.Predicate(node) { - d.posit = 1 - positmap[d.level] = 1 + if d.Self && d.Predicate(node) { return node } } @@ -260,7 +441,6 @@ func (d *descendantQuery) Select(t iterator) NodeNavigator { for { if node.MoveToChild() { d.level = d.level + 1 - positmap[d.level] = 0 } else { for { if d.level == 0 { @@ -274,8 +454,6 @@ func (d *descendantQuery) Select(t iterator) NodeNavigator { } } if d.Predicate(node) { - positmap[d.level]++ - d.posit = positmap[d.level] return node } } @@ -283,6 +461,7 @@ func (d *descendantQuery) Select(t iterator) NodeNavigator { } if node := d.iterator(); node != nil { + d.posit++ return node } d.iterator = nil @@ -309,7 +488,15 @@ func (d *descendantQuery) depth() int { } func (d *descendantQuery) Clone() query { - return &descendantQuery{Self: d.Self, Input: d.Input.Clone(), Predicate: d.Predicate} + return &descendantQuery{name: d.name, Self: d.Self, Input: d.Input.Clone(), Predicate: d.Predicate} +} + +func (d *descendantQuery) ValueType() resultType { + return xpathResultType.NodeSet +} + +func (d *descendantQuery) Properties() queryProp { + return queryProps.Merge } // followingQuery is an XPath following node query.(following::*|following-sibling::*) @@ -390,6 +577,14 @@ func (f *followingQuery) Clone() query { return &followingQuery{Input: f.Input.Clone(), Sibling: f.Sibling, Predicate: f.Predicate} } +func (f *followingQuery) ValueType() resultType { + return xpathResultType.NodeSet +} + +func (f *followingQuery) Properties() queryProp { + return queryProps.Merge +} + func (f *followingQuery) position() int { return f.posit } @@ -471,6 +666,14 @@ func (p *precedingQuery) Clone() query { return &precedingQuery{Input: p.Input.Clone(), Sibling: p.Sibling, Predicate: p.Predicate} } +func (p *precedingQuery) ValueType() resultType { + return xpathResultType.NodeSet +} + +func (p *precedingQuery) Properties() queryProp { + return queryProps.Merge | queryProps.Reverse +} + func (p *precedingQuery) position() int { return p.posit } @@ -503,6 +706,14 @@ func (p *parentQuery) Clone() query { return &parentQuery{Input: p.Input.Clone(), Predicate: p.Predicate} } +func (p *parentQuery) ValueType() resultType { + return xpathResultType.NodeSet +} + +func (p *parentQuery) Properties() queryProp { + return queryProps.Position | queryProps.Count | queryProps.Cached | queryProps.Merge +} + func (p *parentQuery) Test(n NodeNavigator) bool { return p.Predicate(n) } @@ -539,12 +750,22 @@ func (s *selfQuery) Clone() query { return &selfQuery{Input: s.Input.Clone(), Predicate: s.Predicate} } +func (s *selfQuery) ValueType() resultType { + return xpathResultType.NodeSet +} + +func (s *selfQuery) Properties() queryProp { + return queryProps.Merge +} + // filterQuery is an XPath query for predicate filter. type filterQuery struct { - Input query - Predicate query - posit int - positmap map[int]int + Input query + Predicate query + NoPosition bool + + posit int + positmap map[int]int } func (f *filterQuery) do(t iterator) bool { @@ -602,6 +823,14 @@ func (f *filterQuery) Clone() query { return &filterQuery{Input: f.Input.Clone(), Predicate: f.Predicate.Clone()} } +func (f *filterQuery) ValueType() resultType { + return xpathResultType.NodeSet +} + +func (f *filterQuery) Properties() queryProp { + return (queryProps.Position | f.Input.Properties()) & (queryProps.Reverse | queryProps.Merge) +} + // functionQuery is an XPath function that returns a computed value for // the Evaluate call of the current NodeNavigator node. Select call isn't // applicable for functionQuery. @@ -624,6 +853,14 @@ func (f *functionQuery) Clone() query { return &functionQuery{Input: f.Input.Clone(), Func: f.Func} } +func (f *functionQuery) ValueType() resultType { + return xpathResultType.Any +} + +func (f *functionQuery) Properties() queryProp { + return queryProps.Merge +} + // transformFunctionQuery diffs from functionQuery where the latter computes a scalar // value (number,string,boolean) for the current NodeNavigator node while the former // (transformFunctionQuery) performs a mapping or transform of the current NodeNavigator @@ -652,6 +889,14 @@ func (f *transformFunctionQuery) Clone() query { return &transformFunctionQuery{Input: f.Input.Clone(), Func: f.Func} } +func (f *transformFunctionQuery) ValueType() resultType { + return xpathResultType.Any +} + +func (f *transformFunctionQuery) Properties() queryProp { + return queryProps.Merge +} + // constantQuery is an XPath constant operand. type constantQuery struct { Val interface{} @@ -669,6 +914,14 @@ func (c *constantQuery) Clone() query { return c } +func (c *constantQuery) ValueType() resultType { + return getXPathType(c.Val) +} + +func (c *constantQuery) Properties() queryProp { + return queryProps.Position | queryProps.Count | queryProps.Cached | queryProps.Merge +} + type groupQuery struct { posit int @@ -692,6 +945,14 @@ func (g *groupQuery) Clone() query { return &groupQuery{Input: g.Input.Clone()} } +func (g *groupQuery) ValueType() resultType { + return g.Input.ValueType() +} + +func (g *groupQuery) Properties() queryProp { + return queryProps.Position +} + func (g *groupQuery) position() int { return g.posit } @@ -726,11 +987,19 @@ func (l *logicalQuery) Clone() query { return &logicalQuery{Left: l.Left.Clone(), Right: l.Right.Clone(), Do: l.Do} } +func (l *logicalQuery) ValueType() resultType { + return xpathResultType.Boolean +} + +func (l *logicalQuery) Properties() queryProp { + return queryProps.Merge +} + // numericQuery is an XPath numeric operator expression. type numericQuery struct { Left, Right query - Do func(interface{}, interface{}) interface{} + Do func(iterator, interface{}, interface{}) interface{} } func (n *numericQuery) Select(t iterator) NodeNavigator { @@ -740,13 +1009,21 @@ func (n *numericQuery) Select(t iterator) NodeNavigator { func (n *numericQuery) Evaluate(t iterator) interface{} { m := n.Left.Evaluate(t) k := n.Right.Evaluate(t) - return n.Do(m, k) + return n.Do(t, m, k) } func (n *numericQuery) Clone() query { return &numericQuery{Left: n.Left.Clone(), Right: n.Right.Clone(), Do: n.Do} } +func (n *numericQuery) ValueType() resultType { + return xpathResultType.Number +} + +func (n *numericQuery) Properties() queryProp { + return queryProps.Merge +} + type booleanQuery struct { IsOr bool Left, Right query @@ -837,6 +1114,14 @@ func (b *booleanQuery) Clone() query { return &booleanQuery{IsOr: b.IsOr, Left: b.Left.Clone(), Right: b.Right.Clone()} } +func (b *booleanQuery) ValueType() resultType { + return xpathResultType.Boolean +} + +func (b *booleanQuery) Properties() queryProp { + return queryProps.Merge +} + type unionQuery struct { Left, Right query iterator func() NodeNavigator @@ -894,6 +1179,14 @@ func (u *unionQuery) Clone() query { return &unionQuery{Left: u.Left.Clone(), Right: u.Right.Clone()} } +func (u *unionQuery) ValueType() resultType { + return xpathResultType.NodeSet +} + +func (u *unionQuery) Properties() queryProp { + return queryProps.Merge +} + type lastQuery struct { buffer []NodeNavigator counted bool @@ -923,6 +1216,147 @@ func (q *lastQuery) Clone() query { return &lastQuery{Input: q.Input.Clone()} } +func (q *lastQuery) ValueType() resultType { + return xpathResultType.Number +} + +func (q *lastQuery) Properties() queryProp { + return queryProps.Merge +} + +type descendantOverDescendantQuery struct { + name string + level int + posit int + currentNode NodeNavigator + + Input query + MatchSelf bool + Predicate func(NodeNavigator) bool +} + +func (d *descendantOverDescendantQuery) moveToFirstChild() bool { + if d.currentNode.MoveToChild() { + d.level++ + return true + } + return false +} + +func (d *descendantOverDescendantQuery) moveUpUntilNext() bool { + for !d.currentNode.MoveToNext() { + d.level-- + if d.level == 0 { + return false + } + d.currentNode.MoveToParent() + } + return true +} + +func (d *descendantOverDescendantQuery) Select(t iterator) NodeNavigator { + for { + if d.level == 0 { + node := d.Input.Select(t) + if node == nil { + return nil + } + d.currentNode = node.Copy() + d.posit = 0 + if d.MatchSelf && d.Predicate(d.currentNode) { + d.posit = 1 + return d.currentNode + } + d.moveToFirstChild() + } else if !d.moveUpUntilNext() { + continue + } + for ok := true; ok; ok = d.moveToFirstChild() { + if d.Predicate(d.currentNode) { + d.posit++ + return d.currentNode + } + } + } +} + +func (d *descendantOverDescendantQuery) Evaluate(t iterator) interface{} { + d.Input.Evaluate(t) + return d +} + +func (d *descendantOverDescendantQuery) Clone() query { + return &descendantOverDescendantQuery{Input: d.Input.Clone(), Predicate: d.Predicate, MatchSelf: d.MatchSelf} +} + +func (d *descendantOverDescendantQuery) ValueType() resultType { + return xpathResultType.NodeSet +} + +func (d *descendantOverDescendantQuery) Properties() queryProp { + return queryProps.Merge +} + +func (d *descendantOverDescendantQuery) position() int { + return d.posit +} + +type mergeQuery struct { + Input query + Child query + + iterator func() NodeNavigator +} + +func (m *mergeQuery) Select(t iterator) NodeNavigator { + for { + if m.iterator == nil { + root := m.Input.Select(t) + if root == nil { + return nil + } + m.Child.Evaluate(t) + root = root.Copy() + t.Current().MoveTo(root) + var list []NodeNavigator + for node := m.Child.Select(t); node != nil; node = m.Child.Select(t) { + list = append(list, node.Copy()) + } + i := 0 + m.iterator = func() NodeNavigator { + if i >= len(list) { + return nil + } + result := list[i] + i++ + return result + } + } + + if node := m.iterator(); node != nil { + return node + } + m.iterator = nil + } +} + +func (m *mergeQuery) Evaluate(t iterator) interface{} { + m.Input.Evaluate(t) + return m +} + +func (m *mergeQuery) Clone() query { + return &mergeQuery{Input: m.Input.Clone(), Child: m.Child.Clone()} +} + +func (m *mergeQuery) ValueType() resultType { + return xpathResultType.NodeSet +} + +func (m *mergeQuery) Properties() queryProp { + return queryProps.Position | queryProps.Count | queryProps.Cached | queryProps.Merge +} + func getHashCode(n NodeNavigator) uint64 { var sb bytes.Buffer switch n.NodeType() { @@ -958,7 +1392,7 @@ func getHashCode(n NodeNavigator) uint64 { } } h := fnv.New64a() - h.Write([]byte(sb.String())) + h.Write(sb.Bytes()) return h.Sum64() } @@ -981,3 +1415,20 @@ func getNodeDepth(q query) int { } return 0 } + +func getXPathType(i interface{}) resultType { + v := reflect.ValueOf(i) + switch v.Kind() { + case reflect.Float64: + return xpathResultType.Number + case reflect.String: + return xpathResultType.String + case reflect.Bool: + return xpathResultType.Boolean + default: + if _, ok := i.(query); ok { + return xpathResultType.NodeSet + } + } + panic(fmt.Errorf("xpath unknown value type: %v", v.Kind())) +} diff --git a/vendor/github.com/antchfx/xpath/xpath.go b/vendor/github.com/antchfx/xpath/xpath.go index 1c0a5a2..04bbe8d 100644 --- a/vendor/github.com/antchfx/xpath/xpath.go +++ b/vendor/github.com/antchfx/xpath/xpath.go @@ -84,13 +84,13 @@ func (t *NodeIterator) Current() NodeNavigator { // MoveNext moves Navigator to the next match node. func (t *NodeIterator) MoveNext() bool { n := t.query.Select(t) - if n != nil { - if !t.node.MoveTo(n) { - t.node = n.Copy() - } - return true + if n == nil { + return false } - return false + if !t.node.MoveTo(n) { + t.node = n.Copy() + } + return true } // Select selects a node set using the specified XPath expression. diff --git a/vendor/golang.org/x/net/LICENSE b/vendor/golang.org/x/net/LICENSE index 6a66aea..2a7cf70 100644 --- a/vendor/golang.org/x/net/LICENSE +++ b/vendor/golang.org/x/net/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. +Copyright 2009 The Go Authors. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -10,7 +10,7 @@ notice, this list of conditions and the following disclaimer. copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Google Inc. nor the names of its + * Neither the name of Google LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/vendor/golang.org/x/net/html/doc.go b/vendor/golang.org/x/net/html/doc.go index 2466ae3..3a7e5ab 100644 --- a/vendor/golang.org/x/net/html/doc.go +++ b/vendor/golang.org/x/net/html/doc.go @@ -104,7 +104,7 @@ tokenization, and tokenization and tree construction stages of the WHATWG HTML parsing specification respectively. While the tokenizer parses and normalizes individual HTML tokens, only the parser constructs the DOM tree from the tokenized HTML, as described in the tree construction stage of the -specification, dynamically modifying or extending the docuemnt's DOM tree. +specification, dynamically modifying or extending the document's DOM tree. If your use case requires semantically well-formed HTML documents, as defined by the WHATWG specification, the parser should be used rather than the tokenizer. diff --git a/vendor/golang.org/x/net/html/token.go b/vendor/golang.org/x/net/html/token.go index de67f93..3c57880 100644 --- a/vendor/golang.org/x/net/html/token.go +++ b/vendor/golang.org/x/net/html/token.go @@ -910,9 +910,6 @@ func (z *Tokenizer) readTagAttrKey() { return } switch c { - case ' ', '\n', '\r', '\t', '\f', '/': - z.pendingAttr[0].end = z.raw.end - 1 - return case '=': if z.pendingAttr[0].start+1 == z.raw.end { // WHATWG 13.2.5.32, if we see an equals sign before the attribute name @@ -920,7 +917,9 @@ func (z *Tokenizer) readTagAttrKey() { continue } fallthrough - case '>': + case ' ', '\n', '\r', '\t', '\f', '/', '>': + // WHATWG 13.2.5.33 Attribute name state + // We need to reconsume the char in the after attribute name state to support the / character z.raw.end-- z.pendingAttr[0].end = z.raw.end return @@ -939,6 +938,11 @@ func (z *Tokenizer) readTagAttrVal() { if z.err != nil { return } + if c == '/' { + // WHATWG 13.2.5.34 After attribute name state + // U+002F SOLIDUS (/) - Switch to the self-closing start tag state. + return + } if c != '=' { z.raw.end-- return diff --git a/vendor/golang.org/x/text/LICENSE b/vendor/golang.org/x/text/LICENSE index 6a66aea..2a7cf70 100644 --- a/vendor/golang.org/x/text/LICENSE +++ b/vendor/golang.org/x/text/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. +Copyright 2009 The Go Authors. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -10,7 +10,7 @@ notice, this list of conditions and the following disclaimer. copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Google Inc. nor the names of its + * Neither the name of Google LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/vendor/modules.txt b/vendor/modules.txt index 9fb0566..7d37199 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,7 +1,7 @@ -# github.com/antchfx/xmlquery v1.3.18 +# github.com/antchfx/xmlquery v1.4.1 ## explicit; go 1.14 github.com/antchfx/xmlquery -# github.com/antchfx/xpath v1.2.5 +# github.com/antchfx/xpath v1.3.1 ## explicit; go 1.14 github.com/antchfx/xpath # github.com/davecgh/go-spew v1.1.1 @@ -16,12 +16,12 @@ github.com/influxdata/influxdb1-client/pkg/escape github.com/influxdata/influxdb1-client/v2 # github.com/stretchr/testify v1.7.0 ## explicit; go 1.13 -# golang.org/x/net v0.20.0 +# golang.org/x/net v0.28.0 ## explicit; go 1.18 golang.org/x/net/html golang.org/x/net/html/atom golang.org/x/net/html/charset -# golang.org/x/text v0.14.0 +# golang.org/x/text v0.17.0 ## explicit; go 1.18 golang.org/x/text/encoding golang.org/x/text/encoding/charmap