This commit is contained in:
parent
810583964c
commit
6758df2b1d
10
go.mod
10
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
|
||||
)
|
||||
|
||||
|
8
go.sum
8
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=
|
||||
|
17
vendor/github.com/antchfx/xmlquery/.travis.yml
generated
vendored
17
vendor/github.com/antchfx/xmlquery/.travis.yml
generated
vendored
@ -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
|
49
vendor/github.com/antchfx/xmlquery/README.md
generated
vendored
49
vendor/github.com/antchfx/xmlquery/README.md
generated
vendored
@ -1,12 +1,10 @@
|
||||
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
|
||||
data or evaluate from XML documents with an XPath expression.
|
||||
@ -23,15 +21,13 @@ You can visit this page to learn about the supported XPath(1.0/2.0) syntax. http
|
||||
|
||||
[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:
|
||||
<?xml version="1.0"?><rss><channel><title>W3Schools Home Page</title></channel></rss>
|
||||
```
|
||||
|
||||
FAQ
|
||||
====
|
||||
# FAQ
|
||||
|
||||
#### `Find()` vs `QueryAll()`, which is better?
|
||||
|
||||
@ -290,6 +297,6 @@ accept your query expression object.
|
||||
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
|
||||
|
121
vendor/github.com/antchfx/xmlquery/books.xml
generated
vendored
121
vendor/github.com/antchfx/xmlquery/books.xml
generated
vendored
@ -1,121 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet type="text/xsl" ?>
|
||||
<bookstore specialty="novel">
|
||||
<book id="bk101">
|
||||
<author>Gambardella, Matthew</author>
|
||||
<title>XML Developer's Guide</title>
|
||||
<genre>Computer</genre>
|
||||
<price>44.95</price>
|
||||
<publish_date>2000-10-01</publish_date>
|
||||
<description>An in-depth look at creating applications
|
||||
with XML.</description>
|
||||
</book>
|
||||
<book id="bk102">
|
||||
<author>Ralls, Kim</author>
|
||||
<title>Midnight Rain</title>
|
||||
<genre>Fantasy</genre>
|
||||
<price>5.95</price>
|
||||
<publish_date>2000-12-16</publish_date>
|
||||
<description>A former architect battles corporate zombies,
|
||||
an evil sorceress, and her own childhood to become queen
|
||||
of the world.</description>
|
||||
</book>
|
||||
<book id="bk103">
|
||||
<author>Corets, Eva</author>
|
||||
<title>Maeve Ascendant</title>
|
||||
<genre>Fantasy</genre>
|
||||
<price>5.95</price>
|
||||
<publish_date>2000-11-17</publish_date>
|
||||
<description>After the collapse of a nanotechnology
|
||||
society in England, the young survivors lay the
|
||||
foundation for a new society.</description>
|
||||
</book>
|
||||
<book id="bk104">
|
||||
<author>Corets, Eva</author>
|
||||
<title>Oberon's Legacy</title>
|
||||
<genre>Fantasy</genre>
|
||||
<price>5.95</price>
|
||||
<publish_date>2001-03-10</publish_date>
|
||||
<description>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.</description>
|
||||
</book>
|
||||
<book id="bk105">
|
||||
<author>Corets, Eva</author>
|
||||
<title>The Sundered Grail</title>
|
||||
<genre>Fantasy</genre>
|
||||
<price>5.95</price>
|
||||
<publish_date>2001-09-10</publish_date>
|
||||
<description>The two daughters of Maeve, half-sisters,
|
||||
battle one another for control of England. Sequel to
|
||||
Oberon's Legacy.</description>
|
||||
</book>
|
||||
<book id="bk106">
|
||||
<author>Randall, Cynthia</author>
|
||||
<title>Lover Birds</title>
|
||||
<genre>Romance</genre>
|
||||
<price>4.95</price>
|
||||
<publish_date>2000-09-02</publish_date>
|
||||
<description>When Carla meets Paul at an ornithology
|
||||
conference, tempers fly as feathers get ruffled.</description>
|
||||
</book>
|
||||
<book id="bk107">
|
||||
<author>Thurman, Paula</author>
|
||||
<title>Splish Splash</title>
|
||||
<genre>Romance</genre>
|
||||
<price>4.95</price>
|
||||
<publish_date>2000-11-02</publish_date>
|
||||
<description>A deep sea diver finds true love twenty
|
||||
thousand leagues beneath the sea.</description>
|
||||
</book>
|
||||
<book id="bk108">
|
||||
<author>Knorr, Stefan</author>
|
||||
<title>Creepy Crawlies</title>
|
||||
<genre>Horror</genre>
|
||||
<price>4.95</price>
|
||||
<publish_date>2000-12-06</publish_date>
|
||||
<description>An anthology of horror stories about roaches,
|
||||
centipedes, scorpions and other insects.</description>
|
||||
</book>
|
||||
<book id="bk109">
|
||||
<author>Kress, Peter</author>
|
||||
<title>Paradox Lost</title>
|
||||
<genre>Science Fiction</genre>
|
||||
<price>6.95</price>
|
||||
<publish_date>2000-11-02</publish_date>
|
||||
<description>After an inadvertant trip through a Heisenberg
|
||||
Uncertainty Device, James Salway discovers the problems
|
||||
of being quantum.</description>
|
||||
</book>
|
||||
<book id="bk110">
|
||||
<author>O'Brien, Tim</author>
|
||||
<title>Microsoft .NET: The Programming Bible</title>
|
||||
<genre>Computer</genre>
|
||||
<price>36.95</price>
|
||||
<publish_date>2000-12-09</publish_date>
|
||||
<description>Microsoft's .NET initiative is explored in
|
||||
detail in this deep programmer's reference.</description>
|
||||
</book>
|
||||
<book id="bk111">
|
||||
<author>O'Brien, Tim</author>
|
||||
<title>MSXML3: A Comprehensive Guide</title>
|
||||
<genre>Computer</genre>
|
||||
<price>36.95</price>
|
||||
<publish_date>2000-12-01</publish_date>
|
||||
<description>The Microsoft MSXML3 parser is covered in
|
||||
detail, with attention to XML DOM interfaces, XSLT processing,
|
||||
SAX and more.</description>
|
||||
</book>
|
||||
<book id="bk112">
|
||||
<author>Galos, Mike</author>
|
||||
<title>Visual Studio 7: A Comprehensive Guide</title>
|
||||
<genre>Computer</genre>
|
||||
<price>49.95</price>
|
||||
<publish_date>2001-04-16</publish_date>
|
||||
<description>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.</description>
|
||||
</book>
|
||||
</bookstore>
|
9
vendor/github.com/antchfx/xmlquery/node.go
generated
vendored
9
vendor/github.com/antchfx/xmlquery/node.go
generated
vendored
@ -27,6 +27,8 @@ const (
|
||||
CommentNode
|
||||
// AttributeNode is an attribute of element.
|
||||
AttributeNode
|
||||
// NotationNode is a directive represents in document (for example, <!text...>).
|
||||
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, "<!%s>", n.Data)
|
||||
return
|
||||
case DeclarationNode:
|
||||
b.WriteString("<?" + n.Data)
|
||||
default:
|
||||
|
51
vendor/github.com/antchfx/xmlquery/parse.go
generated
vendored
51
vendor/github.com/antchfx/xmlquery/parse.go
generated
vendored
@ -8,6 +8,7 @@ import (
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/antchfx/xpath"
|
||||
"golang.org/x/net/html/charset"
|
||||
@ -59,6 +60,13 @@ type parser struct {
|
||||
streamNode *Node // Need to remember the last target node So we can clean it up upon next Read() call.
|
||||
streamNodePrev *Node // Need to remember target node's prev so upon target node removal, we can restore correct prev.
|
||||
reader *cachedReader // Need to maintain a reference to the reader, so we can determine whether a node contains CDATA.
|
||||
once sync.Once
|
||||
space2prefix map[string]*xmlnsPrefix
|
||||
}
|
||||
|
||||
type xmlnsPrefix struct {
|
||||
name string
|
||||
level int
|
||||
}
|
||||
|
||||
func createParser(r io.Reader) *parser {
|
||||
@ -77,9 +85,11 @@ func createParser(r io.Reader) *parser {
|
||||
}
|
||||
|
||||
func (p *parser) parse() (*Node, error) {
|
||||
var streamElementNodeCounter int
|
||||
space2prefix := map[string]string{"http://www.w3.org/XML/1998/namespace": "xml"}
|
||||
p.once.Do(func() {
|
||||
p.space2prefix = map[string]*xmlnsPrefix{"http://www.w3.org/XML/1998/namespace": {name: "xml", level: 0}}
|
||||
})
|
||||
|
||||
var streamElementNodeCounter int
|
||||
for {
|
||||
p.reader.StartCaching()
|
||||
tok, err := p.decoder.Token()
|
||||
@ -108,16 +118,18 @@ func (p *parser) parse() (*Node, error) {
|
||||
|
||||
for _, att := range tok.Attr {
|
||||
if att.Name.Local == "xmlns" {
|
||||
space2prefix[att.Value] = "" // reset empty if exist the default namespace
|
||||
// defaultNamespaceURL = att.Value
|
||||
// https://github.com/antchfx/xmlquery/issues/67
|
||||
if prefix, ok := p.space2prefix[att.Value]; !ok || (ok && prefix.level >= 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,6 +308,7 @@ type StreamParser struct {
|
||||
// scenarios.
|
||||
//
|
||||
// Scenario 1: simple case:
|
||||
//
|
||||
// xml := `<AAA><BBB>b1</BBB><BBB>b2</BBB></AAA>`
|
||||
// sp, err := CreateStreamParser(strings.NewReader(xml), "/AAA/BBB")
|
||||
// if err != nil {
|
||||
@ -297,11 +321,14 @@ type StreamParser struct {
|
||||
// }
|
||||
// fmt.Println(n.OutputXML(true))
|
||||
// }
|
||||
//
|
||||
// Output will be:
|
||||
//
|
||||
// <BBB>b1</BBB>
|
||||
// <BBB>b2</BBB>
|
||||
//
|
||||
// Scenario 2: advanced case:
|
||||
//
|
||||
// xml := `<AAA><BBB>b1</BBB><BBB>b2</BBB></AAA>`
|
||||
// sp, err := CreateStreamParser(strings.NewReader(xml), "/AAA/BBB", "/AAA/BBB[. != 'b1']")
|
||||
// if err != nil {
|
||||
@ -314,7 +341,9 @@ type StreamParser struct {
|
||||
// }
|
||||
// fmt.Println(n.OutputXML(true))
|
||||
// }
|
||||
//
|
||||
// Output will be:
|
||||
//
|
||||
// <BBB>b2</BBB>
|
||||
//
|
||||
// As the argument names indicate, streamElementXPath should be used for
|
||||
|
2
vendor/github.com/antchfx/xmlquery/query.go
generated
vendored
2
vendor/github.com/antchfx/xmlquery/query.go
generated
vendored
@ -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
|
||||
|
12
vendor/github.com/antchfx/xpath/.travis.yml
generated
vendored
12
vendor/github.com/antchfx/xpath/.travis.yml
generated
vendored
@ -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
|
128
vendor/github.com/antchfx/xpath/README.md
generated
vendored
128
vendor/github.com/antchfx/xpath/README.md
generated
vendored
@ -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.
|
||||
|
||||
@ -93,21 +94,21 @@ Supported Features
|
||||
|
||||
- `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.
|
||||
|
||||
@ -118,48 +119,49 @@ 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()` | ✗ |
|
||||
| ----------------------- | --------- |
|
||||
| `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
|
383
vendor/github.com/antchfx/xpath/build.go
generated
vendored
383
vendor/github.com/antchfx/xpath/build.go
generated
vendored
@ -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)
|
||||
)
|
||||
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,14 +426,13 @@ 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"),
|
||||
}
|
||||
case "string-length":
|
||||
@ -270,16 +440,16 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) {
|
||||
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:
|
||||
//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}
|
||||
}
|
||||
|
||||
//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}
|
||||
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)
|
||||
}
|
||||
|
8
vendor/github.com/antchfx/xpath/func.go
generated
vendored
8
vendor/github.com/antchfx/xpath/func.go
generated
vendored
@ -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))
|
||||
}
|
||||
|
86
vendor/github.com/antchfx/xpath/operator.go
generated
vendored
86
vendor/github.com/antchfx/xpath/operator.go
generated
vendored
@ -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))
|
||||
})
|
||||
}
|
||||
|
519
vendor/github.com/antchfx/xpath/query.go
generated
vendored
519
vendor/github.com/antchfx/xpath/query.go
generated
vendored
@ -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 {
|
||||
func (c *contextQuery) Select(t iterator) NodeNavigator {
|
||||
if c.count > 0 {
|
||||
return nil
|
||||
}
|
||||
c.count++
|
||||
n = t.Current().Copy()
|
||||
if c.Root {
|
||||
n.MoveToRoot()
|
||||
}
|
||||
}
|
||||
return n
|
||||
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,25 +166,28 @@ 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 nil
|
||||
}
|
||||
}
|
||||
|
||||
if node := a.iterator(); node != nil {
|
||||
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,10 +750,20 @@ 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
|
||||
NoPosition bool
|
||||
|
||||
posit int
|
||||
positmap map[int]int
|
||||
}
|
||||
@ -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()))
|
||||
}
|
||||
|
6
vendor/github.com/antchfx/xpath/xpath.go
generated
vendored
6
vendor/github.com/antchfx/xpath/xpath.go
generated
vendored
@ -84,14 +84,14 @@ 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 n == nil {
|
||||
return false
|
||||
}
|
||||
if !t.node.MoveTo(n) {
|
||||
t.node = n.Copy()
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Select selects a node set using the specified XPath expression.
|
||||
// This method is deprecated, recommend using Expr.Select() method instead.
|
||||
|
4
vendor/golang.org/x/net/LICENSE
generated
vendored
4
vendor/golang.org/x/net/LICENSE
generated
vendored
@ -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.
|
||||
|
||||
|
2
vendor/golang.org/x/net/html/doc.go
generated
vendored
2
vendor/golang.org/x/net/html/doc.go
generated
vendored
@ -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.
|
||||
|
12
vendor/golang.org/x/net/html/token.go
generated
vendored
12
vendor/golang.org/x/net/html/token.go
generated
vendored
@ -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
|
||||
|
4
vendor/golang.org/x/text/LICENSE
generated
vendored
4
vendor/golang.org/x/text/LICENSE
generated
vendored
@ -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.
|
||||
|
||||
|
8
vendor/modules.txt
vendored
8
vendor/modules.txt
vendored
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user