Compare commits

..

4 Commits

Author SHA1 Message Date
6758df2b1d updated dependencies
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-19 03:44:45 +02:00
810583964c updated dependencies
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-01 19:16:57 +01:00
ed361c8e9e updated dependencies
All checks were successful
continuous-integration/drone/push Build is passing
2023-10-01 12:01:06 +02:00
d62d777f05 updated dependencies
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-17 13:26:26 +02:00
26 changed files with 3880 additions and 3203 deletions

10
go.mod
View File

@ -1,13 +1,13 @@
module git.paulbsd.com/paulbsd/fuelprices module git.paulbsd.com/paulbsd/fuelprices
go 1.20 go 1.23
require ( require (
github.com/antchfx/xmlquery v1.3.15 github.com/antchfx/xmlquery v1.4.1
github.com/antchfx/xpath v1.2.4 // indirect github.com/antchfx/xpath v1.3.1 // indirect
github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c
golang.org/x/net v0.8.0 // indirect golang.org/x/net v0.28.0 // indirect
golang.org/x/text v0.8.0 // indirect golang.org/x/text v0.17.0 // indirect
gopkg.in/ini.v1 v1.67.0 gopkg.in/ini.v1 v1.67.0
) )

28
go.sum
View File

@ -2,11 +2,19 @@ github.com/antchfx/xmlquery v1.3.12 h1:6TMGpdjpO/P8VhjnaYPXuqT3qyJ/VsqoyNTmJzNBT
github.com/antchfx/xmlquery v1.3.12/go.mod h1:3w2RvQvTz+DaT5fSgsELkSJcdNgkmg6vuXDEuhdwsPQ= github.com/antchfx/xmlquery v1.3.12/go.mod h1:3w2RvQvTz+DaT5fSgsELkSJcdNgkmg6vuXDEuhdwsPQ=
github.com/antchfx/xmlquery v1.3.15 h1:aJConNMi1sMha5G8YJoAIF5P+H+qG1L73bSItWHo8Tw= github.com/antchfx/xmlquery v1.3.15 h1:aJConNMi1sMha5G8YJoAIF5P+H+qG1L73bSItWHo8Tw=
github.com/antchfx/xmlquery v1.3.15/go.mod h1:zMDv5tIGjOxY/JCNNinnle7V/EwthZ5IT8eeCGJKRWA= 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 h1:qhp4EW6aCOVr5XIkT+l6LJ9ck/JsUH/yyauNgTQkBF8=
github.com/antchfx/xpath v1.2.1/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs= github.com/antchfx/xpath v1.2.1/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs=
github.com/antchfx/xpath v1.2.3/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs= github.com/antchfx/xpath v1.2.3/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs=
github.com/antchfx/xpath v1.2.4 h1:dW1HB/JxKvGtJ9WyVGJ0sIoEcqftV3SqIstujI+B9XY= 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.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.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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -31,8 +39,17 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0= golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
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-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/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= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -42,17 +59,28 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
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-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.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=

View File

@ -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

View File

@ -1,12 +1,10 @@
xmlquery # xmlquery
====
[![Build Status](https://travis-ci.org/antchfx/xmlquery.svg?branch=master)](https://travis-ci.org/antchfx/xmlquery) [![Build Status](https://github.com/antchfx/xmlquery/actions/workflows/testing.yml/badge.svg)](https://github.com/antchfx/xmlquery/actions/workflows/testing.yml)
[![Coverage Status](https://coveralls.io/repos/github/antchfx/xmlquery/badge.svg?branch=master)](https://coveralls.io/github/antchfx/xmlquery?branch=master)
[![GoDoc](https://godoc.org/github.com/antchfx/xmlquery?status.svg)](https://godoc.org/github.com/antchfx/xmlquery) [![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) [![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. data or evaluate from XML documents with an XPath expression.
@ -17,21 +15,19 @@ each query.
You can visit this page to learn about the supported XPath(1.0/2.0) syntax. https://github.com/antchfx/xpath 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 $ go get github.com/antchfx/xmlquery
``` ```
# Quick Starts
Quick Starts
===
```go ```go
import ( import (
@ -75,8 +71,7 @@ func main(){
} }
``` ```
Getting Started # Getting Started
===
### Find specified XPath query. ### Find specified XPath query.
@ -110,7 +105,7 @@ doc, err := xmlquery.Parse(f)
#### Parse an XML in a stream fashion (simple case without elements filtering). #### Parse an XML in a stream fashion (simple case without elements filtering).
```go ```go
f, err := os.Open("../books.xml") f, _ := os.Open("../books.xml")
p, err := xmlquery.CreateStreamParser(f, "/bookstore/book") p, err := xmlquery.CreateStreamParser(f, "/bookstore/book")
for { for {
n, err := p.Read() n, err := p.Read()
@ -118,15 +113,18 @@ for {
break break
} }
if err != nil { if err != nil {
... panic(err)
} }
fmt.Println(n)
} }
``` ```
Notes: `CreateStreamParser()` used for saving memory if your had a large XML file to parse.
#### Parse an XML in a stream fashion (simple case advanced element filtering). #### Parse an XML in a stream fashion (simple case advanced element filtering).
```go ```go
f, err := os.Open("../books.xml") f, _ := os.Open("../books.xml")
p, err := xmlquery.CreateStreamParser(f, "/bookstore/book", "/bookstore/book[price>=10]") p, err := xmlquery.CreateStreamParser(f, "/bookstore/book", "/bookstore/book[price>=10]")
for { for {
n, err := p.Read() n, err := p.Read()
@ -134,8 +132,9 @@ for {
break break
} }
if err != nil { if err != nil {
... panic(err)
} }
fmt.Println(n)
} }
``` ```
@ -153,10 +152,17 @@ list := xmlquery.Find(doc, "//author")
book := xmlquery.FindOne(doc, "//book[2]") 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 ```go
list := xmlquery.Find(doc,"//book/@id") list := xmlquery.Find(doc,"//book/@id")
fmt.Println(list[0].InnerText) // outout @id value
``` ```
#### Find all books with id `bk104`. #### Find all books with id `bk104`.
@ -179,31 +185,62 @@ price := expr.Evaluate(xmlquery.CreateXPathNavigator(doc)).(float64)
fmt.Printf("total price: %f\n", price) fmt.Printf("total price: %f\n", price)
``` ```
#### Evaluate number of all book elements. #### Count the number of books.
```go ```go
expr, err := xpath.Compile("count(//book)") 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) price := expr.Evaluate(xmlquery.CreateXPathNavigator(doc)).(float64)
``` ```
FAQ # Advanced Features
====
#### `Find()` vs `QueryAll()`, which is better? ### Parse `UTF-16` XML file with `ParseWithOptions()`.
`Find` and `QueryAll` both do the same thing: searches all of matched XML nodes. ```go
`Find` panics if provided with an invalid XPath query, while `QueryAll` returns f, _ := os.Open(`UTF-16.XML`)
an error. // Convert UTF-16 XML to UTF-8
utf16ToUtf8Transformer := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewDecoder()
utf8Reader := transform.NewReader(f, utf16ToUtf8Transformer)
// Sets `CharsetReader`
options := xmlquery.ParserOptions{
Decoder: &xmlquery.DecoderOptions{
CharsetReader: func(charset string, input io.Reader) (io.Reader, error) {
return input, nil
},
},
}
doc, err := xmlquery.ParseWithOptions(utf8Reader, options)
```
#### Can I save my query expression object for the next query? ### Query with custom namespace prefix.
Yes, you can. We provide `QuerySelector` and `QuerySelectorAll` methods; they ```go
accept your query expression object. s := `<?xml version="1.0" encoding="UTF-8"?>
<pd:ProcessDefinition xmlns:pd="http://xmlns.xyz.com/process/2003" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<pd:activity name="Invoke Request-Response Service">
<pd:type>RequestReplyActivity</pd:type>
<pd:resourceType>OpClientReqActivity</pd:resourceType>
<pd:x>300</pd:x>
<pd:y>80</pd:y>
</pd:activity>
</pd:ProcessDefinition>`
nsMap := map[string]string{
"q": "http://xmlns.xyz.com/process/2003",
"r": "http://www.w3.org/1999/XSL/Transform",
"s": "http://www.w3.org/2001/XMLSchema",
}
expr, _ := xpath.CompileWithNS("//q:activity", nsMap)
node := xmlquery.QuerySelector(doc, expr)
```
Caching a query expression object avoids recompiling the XPath query #### Create XML document without call `xml.Marshal`.
expression, improving query performance.
#### Create XML document.
```go ```go
doc := &xmlquery.Node{ doc := &xmlquery.Node{
@ -233,13 +270,33 @@ title_text := &xmlquery.Node{
} }
title.FirstChild = title_text title.FirstChild = title_text
channel.FirstChild = title channel.FirstChild = title
fmt.Println(doc.OutputXML(true))
// <?xml version="1.0"?><rss><channel><title>W3Schools Home Page</title></channel></rss>
fmt.Println(doc.OutputXML(true))
fmt.Println(doc.OutputXMLWithOptions(WithOutputSelf())) fmt.Println(doc.OutputXMLWithOptions(WithOutputSelf()))
// <?xml version="1.0"?><rss><channel><title>W3Schools Home Page</title></channel></rss>
``` ```
Questions Output:
===
```xml
<?xml version="1.0"?><rss><channel><title>W3Schools Home Page</title></channel></rss>
```
# FAQ
#### `Find()` vs `QueryAll()`, which is better?
`Find` and `QueryAll` both do the same thing: searches all of matched XML nodes.
`Find` panics if provided with an invalid XPath query, while `QueryAll` returns
an error.
#### Can I save my query expression object for the next query?
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
expression, improving query performance.
# Questions
Please let me know if you have any questions Please let me know if you have any questions

View File

@ -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>

View File

@ -27,6 +27,8 @@ const (
CommentNode CommentNode
// AttributeNode is an attribute of element. // AttributeNode is an attribute of element.
AttributeNode AttributeNode
// NotationNode is a directive represents in document (for example, <!text...>).
NotationNode
) )
type Attr struct { type Attr struct {
@ -80,6 +82,29 @@ func WithoutComments() OutputOption {
} }
} }
// WithPreserveSpace will preserve spaces in output
func WithPreserveSpace() OutputOption {
return func(oc *outputConfiguration) {
oc.preserveSpaces = true
}
}
func newXMLName(name string) xml.Name {
if i := strings.IndexByte(name, ':'); i > 0 {
return xml.Name{
Space: name[:i],
Local: name[i+1:],
}
}
return xml.Name{
Local: name,
}
}
func (n *Node) Level() int {
return n.level
}
// InnerText returns the text between the start and end tags of the object. // InnerText returns the text between the start and end tags of the object.
func (n *Node) InnerText() string { func (n *Node) InnerText() string {
var output func(*strings.Builder, *Node) var output func(*strings.Builder, *Node)
@ -134,21 +159,24 @@ func outputXML(b *strings.Builder, n *Node, preserveSpaces bool, config *outputC
b.WriteString("-->") b.WriteString("-->")
} }
return return
case NotationNode:
fmt.Fprintf(b, "<!%s>", n.Data)
return
case DeclarationNode: case DeclarationNode:
b.WriteString("<?" + n.Data) b.WriteString("<?" + n.Data)
default: default:
if n.Prefix == "" { if n.Prefix == "" {
b.WriteString("<" + n.Data) b.WriteString("<" + n.Data)
} else { } else {
b.WriteString("<" + n.Prefix + ":" + n.Data) fmt.Fprintf(b, "<%s:%s", n.Prefix, n.Data)
} }
} }
for _, attr := range n.Attr { for _, attr := range n.Attr {
if attr.Name.Space != "" { if attr.Name.Space != "" {
b.WriteString(fmt.Sprintf(` %s:%s=`, attr.Name.Space, attr.Name.Local)) fmt.Fprintf(b, ` %s:%s=`, attr.Name.Space, attr.Name.Local)
} else { } else {
b.WriteString(fmt.Sprintf(` %s=`, attr.Name.Local)) fmt.Fprintf(b, ` %s=`, attr.Name.Local)
} }
b.WriteByte('"') b.WriteByte('"')
b.WriteString(html.EscapeString(attr.Value)) b.WriteString(html.EscapeString(attr.Value))
@ -169,9 +197,9 @@ func outputXML(b *strings.Builder, n *Node, preserveSpaces bool, config *outputC
} }
if n.Type != DeclarationNode { if n.Type != DeclarationNode {
if n.Prefix == "" { if n.Prefix == "" {
b.WriteString(fmt.Sprintf("</%s>", n.Data)) fmt.Fprintf(b, "</%s>", n.Data)
} else { } else {
b.WriteString(fmt.Sprintf("</%s:%s>", n.Prefix, n.Data)) fmt.Fprintf(b, "</%s:%s>", n.Prefix, n.Data)
} }
} }
} }
@ -204,8 +232,8 @@ func (n *Node) OutputXMLWithOptions(opts ...OutputOption) string {
for _, opt := range opts { for _, opt := range opts {
opt(config) opt(config)
} }
pastPreserveSpaces := config.preserveSpaces
preserveSpaces := calculatePreserveSpaces(n, false) preserveSpaces := calculatePreserveSpaces(n, pastPreserveSpaces)
var b strings.Builder var b strings.Builder
if config.printSelf && n.Type != DocumentNode { if config.printSelf && n.Type != DocumentNode {
outputXML(&b, n, preserveSpaces, config) outputXML(&b, n, preserveSpaces, config)
@ -220,68 +248,34 @@ func (n *Node) OutputXMLWithOptions(opts ...OutputOption) string {
// AddAttr adds a new attribute specified by 'key' and 'val' to a node 'n'. // AddAttr adds a new attribute specified by 'key' and 'val' to a node 'n'.
func AddAttr(n *Node, key, val string) { func AddAttr(n *Node, key, val string) {
var attr Attr attr := Attr{
if i := strings.Index(key, ":"); i > 0 { Name: newXMLName(key),
attr = Attr{ Value: val,
Name: xml.Name{Space: key[:i], Local: key[i+1:]},
Value: val,
}
} else {
attr = Attr{
Name: xml.Name{Local: key},
Value: val,
}
} }
n.Attr = append(n.Attr, attr) n.Attr = append(n.Attr, attr)
} }
// SetAttr allows an attribute value with the specified name to be changed. // SetAttr allows an attribute value with the specified name to be changed.
// If the attribute did not previously exist, it will be created. // If the attribute did not previously exist, it will be created.
func (n *Node) SetAttr(key, value string) { func (n *Node) SetAttr(key, value string) {
if i := strings.Index(key, ":"); i > 0 { name := newXMLName(key)
space := key[:i] for i, attr := range n.Attr {
local := key[i+1:] if attr.Name == name {
for idx := 0; idx < len(n.Attr); idx++ { n.Attr[i].Value = value
if n.Attr[idx].Name.Space == space && n.Attr[idx].Name.Local == local { return
n.Attr[idx].Value = value
return
}
} }
AddAttr(n, key, value)
} else {
for idx := 0; idx < len(n.Attr); idx++ {
if n.Attr[idx].Name.Local == key {
n.Attr[idx].Value = value
return
}
}
AddAttr(n, key, value)
} }
AddAttr(n, key, value)
} }
// RemoveAttr removes the attribute with the specified name. // RemoveAttr removes the attribute with the specified name.
func (n *Node) RemoveAttr(key string) { func (n *Node) RemoveAttr(key string) {
removeIdx := -1 name := newXMLName(key)
if i := strings.Index(key, ":"); i > 0 { for i, attr := range n.Attr {
space := key[:i] if attr.Name == name {
local := key[i+1:] n.Attr = append(n.Attr[:i], n.Attr[i+1:]...)
for idx := 0; idx < len(n.Attr); idx++ { return
if n.Attr[idx].Name.Space == space && n.Attr[idx].Name.Local == local {
removeIdx = idx
}
} }
} else {
for idx := 0; idx < len(n.Attr); idx++ {
if n.Attr[idx].Name.Local == key {
removeIdx = idx
}
}
}
if removeIdx != -1 {
n.Attr = append(n.Attr[:removeIdx], n.Attr[removeIdx+1:]...)
} }
} }

View File

@ -2,9 +2,10 @@ package xmlquery
import ( import (
"encoding/xml" "encoding/xml"
"io"
) )
type ParserOptions struct{ type ParserOptions struct {
Decoder *DecoderOptions Decoder *DecoderOptions
} }
@ -17,14 +18,16 @@ func (options ParserOptions) apply(parser *parser) {
// DecoderOptions implement the very same options than the standard // DecoderOptions implement the very same options than the standard
// encoding/xml package. Please refer to this documentation: // encoding/xml package. Please refer to this documentation:
// https://golang.org/pkg/encoding/xml/#Decoder // https://golang.org/pkg/encoding/xml/#Decoder
type DecoderOptions struct{ type DecoderOptions struct {
Strict bool Strict bool
AutoClose []string AutoClose []string
Entity map[string]string Entity map[string]string
CharsetReader func(charset string, input io.Reader) (io.Reader, error)
} }
func (options DecoderOptions) apply(decoder *xml.Decoder) { func (options DecoderOptions) apply(decoder *xml.Decoder) {
decoder.Strict = options.Strict decoder.Strict = options.Strict
decoder.AutoClose = options.AutoClose decoder.AutoClose = options.AutoClose
decoder.Entity = options.Entity decoder.Entity = options.Entity
decoder.CharsetReader = options.CharsetReader
} }

View File

@ -8,6 +8,7 @@ import (
"net/http" "net/http"
"regexp" "regexp"
"strings" "strings"
"sync"
"github.com/antchfx/xpath" "github.com/antchfx/xpath"
"golang.org/x/net/html/charset" "golang.org/x/net/html/charset"
@ -52,7 +53,6 @@ func ParseWithOptions(r io.Reader, options ParserOptions) (*Node, error) {
type parser struct { type parser struct {
decoder *xml.Decoder decoder *xml.Decoder
doc *Node doc *Node
space2prefix map[string]string
level int level int
prev *Node prev *Node
streamElementXPath *xpath.Expr // Under streaming mode, this specifies the xpath to the target element node(s). streamElementXPath *xpath.Expr // Under streaming mode, this specifies the xpath to the target element node(s).
@ -60,29 +60,40 @@ type parser struct {
streamNode *Node // Need to remember the last target node So we can clean it up upon next Read() call. 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. 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. 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 { func createParser(r io.Reader) *parser {
reader := newCachedReader(bufio.NewReader(r)) reader := newCachedReader(bufio.NewReader(r))
p := &parser{ p := &parser{
decoder: xml.NewDecoder(reader), decoder: xml.NewDecoder(reader),
doc: &Node{Type: DocumentNode}, doc: &Node{Type: DocumentNode},
space2prefix: make(map[string]string), level: 0,
level: 0, reader: reader,
reader: reader, }
if p.decoder.CharsetReader == nil {
p.decoder.CharsetReader = charset.NewReaderLabel
} }
// http://www.w3.org/XML/1998/namespace is bound by definition to the prefix xml.
p.space2prefix["http://www.w3.org/XML/1998/namespace"] = "xml"
p.decoder.CharsetReader = charset.NewReaderLabel
p.prev = p.doc p.prev = p.doc
return p return p
} }
func (p *parser) parse() (*Node, error) { func (p *parser) parse() (*Node, error) {
var streamElementNodeCounter int p.once.Do(func() {
p.space2prefix = map[string]*xmlnsPrefix{"http://www.w3.org/XML/1998/namespace": {name: "xml", level: 0}}
})
var streamElementNodeCounter int
for { for {
p.reader.StartCaching()
tok, err := p.decoder.Token() tok, err := p.decoder.Token()
p.reader.StopCaching()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -104,16 +115,16 @@ func (p *parser) parse() (*Node, error) {
p.level = 1 p.level = 1
p.prev = node p.prev = node
} }
// https://www.w3.org/TR/xml-names/#scoping-defaulting
var defaultNamespaceURL string
for _, att := range tok.Attr { for _, att := range tok.Attr {
if att.Name.Local == "xmlns" { if att.Name.Local == "xmlns" {
p.space2prefix[att.Value] = "" // reset empty if exist the default namespace // https://github.com/antchfx/xmlquery/issues/67
defaultNamespaceURL = att.Value if prefix, ok := p.space2prefix[att.Value]; !ok || (ok && prefix.level >= p.level) {
} else if att.Name.Space == "xmlns" { p.space2prefix[att.Value] = &xmlnsPrefix{name: "", level: p.level} // reset empty if exist the default namespace
if _, ok := p.space2prefix[att.Value]; !ok {
p.space2prefix[att.Value] = att.Name.Local
} }
} else if att.Name.Space == "xmlns" {
// maybe there are have duplicate NamespaceURL?
p.space2prefix[att.Value] = &xmlnsPrefix{name: att.Name.Local, level: p.level}
} }
} }
@ -127,7 +138,7 @@ func (p *parser) parse() (*Node, error) {
for i, att := range tok.Attr { for i, att := range tok.Attr {
name := att.Name name := att.Name
if prefix, ok := p.space2prefix[name.Space]; ok { if prefix, ok := p.space2prefix[name.Space]; ok {
name.Space = prefix name.Space = prefix.name
} }
attributes[i] = Attr{ attributes[i] = Attr{
Name: name, Name: name,
@ -154,12 +165,13 @@ func (p *parser) parse() (*Node, error) {
} }
AddSibling(p.prev.Parent, node) AddSibling(p.prev.Parent, node)
} }
if node.NamespaceURI != "" { if node.NamespaceURI != "" {
node.Prefix = p.space2prefix[node.NamespaceURI] if v, ok := p.space2prefix[node.NamespaceURI]; ok {
if defaultNamespaceURL != "" && node.NamespaceURI == defaultNamespaceURL { cached := string(p.reader.Cache())
node.Prefix = "" if strings.HasPrefix(cached, fmt.Sprintf("%s:%s", v.name, node.Data)) || strings.HasPrefix(cached, fmt.Sprintf("<%s:%s", v.name, node.Data)) {
} else if n := node.Parent; n != nil && node.NamespaceURI == n.NamespaceURI { node.Prefix = v.name
node.Prefix = n.Prefix }
} }
} }
// If we're in the streaming mode, we need to remember the node if it is the target node // If we're in the streaming mode, we need to remember the node if it is the target node
@ -179,7 +191,6 @@ func (p *parser) parse() (*Node, error) {
} }
p.prev = node p.prev = node
p.level++ p.level++
p.reader.StartCaching()
case xml.EndElement: case xml.EndElement:
p.level-- p.level--
// If we're in streaming mode, and we already have a potential streaming // If we're in streaming mode, and we already have a potential streaming
@ -216,7 +227,6 @@ func (p *parser) parse() (*Node, error) {
} }
} }
case xml.CharData: case xml.CharData:
p.reader.StopCaching()
// First, normalize the cache... // First, normalize the cache...
cached := strings.ToUpper(string(p.reader.Cache())) cached := strings.ToUpper(string(p.reader.Cache()))
nodeType := TextNode nodeType := TextNode
@ -235,7 +245,6 @@ func (p *parser) parse() (*Node, error) {
} }
AddSibling(p.prev.Parent, node) AddSibling(p.prev.Parent, node)
} }
p.reader.StartCaching()
case xml.Comment: case xml.Comment:
node := &Node{Type: CommentNode, Data: string(tok), level: p.level} node := &Node{Type: CommentNode, Data: string(tok), level: p.level}
if p.level == p.prev.level { if p.level == p.prev.level {
@ -272,6 +281,17 @@ func (p *parser) parse() (*Node, error) {
} }
p.prev = node p.prev = node
case xml.Directive: 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)
}
} }
} }
} }
@ -288,37 +308,43 @@ type StreamParser struct {
// scenarios. // scenarios.
// //
// Scenario 1: simple case: // Scenario 1: simple case:
// xml := `<AAA><BBB>b1</BBB><BBB>b2</BBB></AAA>` //
// sp, err := CreateStreamParser(strings.NewReader(xml), "/AAA/BBB") // xml := `<AAA><BBB>b1</BBB><BBB>b2</BBB></AAA>`
// if err != nil { // sp, err := CreateStreamParser(strings.NewReader(xml), "/AAA/BBB")
// panic(err) // if err != nil {
// } // panic(err)
// for { // }
// n, err := sp.Read() // for {
// if err != nil { // n, err := sp.Read()
// break // if err != nil {
// } // break
// fmt.Println(n.OutputXML(true)) // }
// } // fmt.Println(n.OutputXML(true))
// }
//
// Output will be: // Output will be:
// <BBB>b1</BBB> //
// <BBB>b2</BBB> // <BBB>b1</BBB>
// <BBB>b2</BBB>
// //
// Scenario 2: advanced case: // Scenario 2: advanced case:
// xml := `<AAA><BBB>b1</BBB><BBB>b2</BBB></AAA>` //
// sp, err := CreateStreamParser(strings.NewReader(xml), "/AAA/BBB", "/AAA/BBB[. != 'b1']") // xml := `<AAA><BBB>b1</BBB><BBB>b2</BBB></AAA>`
// if err != nil { // sp, err := CreateStreamParser(strings.NewReader(xml), "/AAA/BBB", "/AAA/BBB[. != 'b1']")
// panic(err) // if err != nil {
// } // panic(err)
// for { // }
// n, err := sp.Read() // for {
// if err != nil { // n, err := sp.Read()
// break // if err != nil {
// } // break
// fmt.Println(n.OutputXML(true)) // }
// } // fmt.Println(n.OutputXML(true))
// }
//
// Output will be: // Output will be:
// <BBB>b2</BBB> //
// <BBB>b2</BBB>
// //
// As the argument names indicate, streamElementXPath should be used for // As the argument names indicate, streamElementXPath should be used for
// providing xpath query pointing to the target element node only, no extra // providing xpath query pointing to the target element node only, no extra

View File

@ -28,14 +28,9 @@ func (n *Node) SelectAttr(name string) string {
} }
return "" return ""
} }
var local, space string xmlName := newXMLName(name)
local = name
if i := strings.Index(name, ":"); i > 0 {
space = name[:i]
local = name[i+1:]
}
for _, attr := range n.Attr { for _, attr := range n.Attr {
if attr.Name.Local == local && attr.Name.Space == space { if attr.Name == xmlName {
return attr.Value return attr.Value
} }
} }
@ -161,7 +156,7 @@ func (x *NodeNavigator) NodeType() xpath.NodeType {
switch x.curr.Type { switch x.curr.Type {
case CommentNode: case CommentNode:
return xpath.CommentNode return xpath.CommentNode
case TextNode, CharDataNode: case TextNode, CharDataNode, NotationNode:
return xpath.TextNode return xpath.TextNode
case DeclarationNode, DocumentNode: case DeclarationNode, DocumentNode:
return xpath.RootNode return xpath.RootNode

View File

@ -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

View File

@ -1,14 +1,13 @@
XPath # XPath
====
[![GoDoc](https://godoc.org/github.com/antchfx/xpath?status.svg)](https://godoc.org/github.com/antchfx/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) [![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) [![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. 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 - [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 - [jsonquery](https://github.com/antchfx/jsonquery) - an XPath query package for JSON document
Supported Features # Supported Features
===
#### The basic XPath patterns. #### The basic XPath patterns.
@ -63,11 +61,14 @@ Supported Features
- `child::*` : The child axis selects children of the current node. - `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::*` : The descendant axis selects descendants of the current node. It is equivalent to '//'.
- `descendant-or-self::*` : Selects descendants including the current node. - `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. - `following-sibling::*` : Selects nodes after the current node.
@ -87,27 +88,27 @@ Supported Features
#### Expressions #### Expressions
The gxpath supported three types: number, boolean, string. The gxpath supported three types: number, boolean, string.
- `path` : Selects nodes based on the path. - `path` : Selects nodes based on the path.
- `a = b` : Standard comparisons. - `a = b` : Standard comparisons.
* a = b True if a equals b. - `a = b` : True if a equals b.
* a != b True if a is not equal to 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 b.
* a <= b True if a is less than or equal to 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 b.
* a >= b True if a is greater than or equal to b. - `a >= b` : True if a is greater than or equal to b.
- `a + b` : Arithmetic expressions. - `a + b` : Arithmetic expressions.
* `- a` Unary minus - `- a` Unary minus
* a + b Add - `a + b` : Addition
* a - b Substract - `a - b` : Subtraction
* a * b Multiply - `a * b` : Multiplication
* a div b Divide - `a div b` : Division
* a mod b Floating point mod, like Java. - `a mod b` : Modulus (division remainder)
- `a or b` : Boolean `or` operation. - `a or b` : Boolean `or` operation.
@ -117,46 +118,50 @@ Supported Features
- `fun(arg1, ..., argn)` : Function calls: - `fun(arg1, ..., argn)` : Function calls:
| Function | Supported | | Function | Supported |
| --- | --- | | ----------------------- | --------- |
`boolean()`| ✓ | | `boolean()` | ✓ |
`ceiling()`| ✓ | | `ceiling()` | ✓ |
`choose()`| ✗ | | `choose()` | ✗ |
`concat()`| ✓ | | `concat()` | ✓ |
`contains()`| ✓ | | `contains()` | ✓ |
`count()`| ✓ | | `count()` | ✓ |
`current()`| ✗ | | `current()` | ✗ |
`document()`| ✗ | | `document()` | ✗ |
`element-available()`| ✗ | | `element-available()` | ✗ |
`ends-with()`| ✓ | | `ends-with()` | ✓ |
`false()`| ✓ | | `false()` | ✓ |
`floor()`| ✓ | | `floor()` | ✓ |
`format-number()`| ✗ | | `format-number()` | ✗ |
`function-available()`| ✗ | | `function-available()` | ✗ |
`generate-id()`| ✗ | | `generate-id()` | ✗ |
`id()`| ✗ | | `id()` | ✗ |
`key()`| ✗ | | `key()` | ✗ |
`lang()`| ✗ | | `lang()` | ✗ |
`last()`| ✓ | | `last()` | ✓ |
`local-name()`| ✓ | | `local-name()` | ✓ |
`matches()`| ✓ | | `lower-case()`[^1] | ✓ |
`name()`| ✓ | | `matches()` | ✓ |
`namespace-uri()`| ✓ | | `name()` | ✓ |
`normalize-space()`| ✓ | | `namespace-uri()` | ✓ |
`not()`| ✓ | | `normalize-space()` | ✓ |
`number()`| ✓ | | `not()` | ✓ |
`position()`| ✓ | | `number()` | ✓ |
`replace()`| ✓ | | `position()` | ✓ |
`reverse()`| ✓ | | `replace()` | ✓ |
`round()`| ✓ | | `reverse()` | ✓ |
`starts-with()`| ✓ | | `round()` | ✓ |
`string()`| ✓ | | `starts-with()` | ✓ |
`string-length()`| ✓ | | `string()` | ✓ |
`substring()`| ✓ | | `string-join()`[^1] | ✓ |
`substring-after()`| ✓ | | `string-length()` | ✓ |
`substring-before()`| ✓ | | `substring()` | ✓ |
`sum()`| ✓ | | `substring-after()` | ✓ |
`system-property()`| ✗ | | `substring-before()` | ✓ |
`translate()`| ✓ | | `sum()` | ✓ |
`true()`| ✓ | | `system-property()` | ✗ |
`unparsed-entity-url()` | ✗ | | `translate()` | ✓ |
| `true()` | ✓ |
| `unparsed-entity-url()` | ✗ |
[^1]: XPath-2.0 expression

View File

@ -7,15 +7,39 @@ import (
type flag int type flag int
const ( var flagsEnum = struct {
noneFlag flag = iota None flag
filterFlag 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. // builder provides building an XPath expressions.
type builder struct { type builder struct {
depth int parseDepth int
flag flag
firstInput query firstInput query
} }
@ -63,23 +87,26 @@ func axisPredicate(root *axisNode) func(NodeNavigator) bool {
return predicate return predicate
} }
// processAxisNode processes a query for the XPath axis node. // processAxis processes a query for the XPath axis node.
func (b *builder) processAxisNode(root *axisNode) (query, error) { func (b *builder) processAxis(root *axisNode, flags flag, props *builderProp) (query, error) {
var ( var (
err error err error
qyInput query qyInput query
qyOutput query qyOutput query
predicate = axisPredicate(root)
) )
b.firstInput = nil
predicate := axisPredicate(root)
if root.Input == nil { if root.Input == nil {
qyInput = &contextQuery{} qyInput = &contextQuery{}
*props = builderProps.None
} else { } else {
inputFlags := flagsEnum.None
if root.AxeType == "child" && (root.Input.Type() == nodeAxis) { if root.AxeType == "child" && (root.Input.Type() == nodeAxis) {
if input := root.Input.(*axisNode); input.AxeType == "descendant-or-self" { if input := root.Input.(*axisNode); input.AxeType == "descendant-or-self" {
var qyGrandInput query var qyGrandInput query
if input.Input != nil { if input.Input != nil {
qyGrandInput, _ = b.processNode(input.Input) qyGrandInput, _ = b.processNode(input.Input, flagsEnum.SmartDesc, props)
} else { } else {
qyGrandInput = &contextQuery{} qyGrandInput = &contextQuery{}
} }
@ -94,14 +121,14 @@ func (b *builder) processAxisNode(root *axisNode) (query, error) {
} }
return v return v
} }
// fix `//*[contains(@id,"food")]//*[contains(@id,"food")]`, see https://github.com/antchfx/htmlquery/issues/52 qyOutput = &descendantQuery{name: root.LocalName, Input: qyGrandInput, Predicate: filter, Self: false}
// Skip the current node(Self:false) for the next descendants nodes. *props |= builderProps.NonFlat
_, ok := qyGrandInput.(*contextQuery)
qyOutput = &descendantQuery{Input: qyGrandInput, Predicate: filter, Self: ok}
return qyOutput, nil 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 { if err != nil {
return nil, err return nil, err
} }
@ -109,11 +136,13 @@ func (b *builder) processAxisNode(root *axisNode) (query, error) {
switch root.AxeType { switch root.AxeType {
case "ancestor": case "ancestor":
qyOutput = &ancestorQuery{Input: qyInput, Predicate: predicate} qyOutput = &ancestorQuery{name: root.LocalName, Input: qyInput, Predicate: predicate}
*props |= builderProps.NonFlat
case "ancestor-or-self": 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": case "attribute":
qyOutput = &attributeQuery{Input: qyInput, Predicate: predicate} qyOutput = &attributeQuery{name: root.LocalName, Input: qyInput, Predicate: predicate}
case "child": case "child":
filter := func(n NodeNavigator) bool { filter := func(n NodeNavigator) bool {
v := predicate(n) v := predicate(n)
@ -127,19 +156,35 @@ func (b *builder) processAxisNode(root *axisNode) (query, error) {
} }
return v 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": 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": 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": case "following":
qyOutput = &followingQuery{Input: qyInput, Predicate: predicate} qyOutput = &followingQuery{Input: qyInput, Predicate: predicate}
*props |= builderProps.NonFlat
case "following-sibling": case "following-sibling":
qyOutput = &followingQuery{Input: qyInput, Predicate: predicate, Sibling: true} qyOutput = &followingQuery{Input: qyInput, Predicate: predicate, Sibling: true}
case "parent": case "parent":
qyOutput = &parentQuery{Input: qyInput, Predicate: predicate} qyOutput = &parentQuery{Input: qyInput, Predicate: predicate}
case "preceding": case "preceding":
qyOutput = &precedingQuery{Input: qyInput, Predicate: predicate} qyOutput = &precedingQuery{Input: qyInput, Predicate: predicate}
*props |= builderProps.NonFlat
case "preceding-sibling": case "preceding-sibling":
qyOutput = &precedingQuery{Input: qyInput, Predicate: predicate, Sibling: true} qyOutput = &precedingQuery{Input: qyInput, Predicate: predicate, Sibling: true}
case "self": case "self":
@ -153,56 +198,182 @@ func (b *builder) processAxisNode(root *axisNode) (query, error) {
return qyOutput, nil return qyOutput, nil
} }
// processFilterNode builds query for the XPath filter predicate. func canBeNumber(q query) bool {
func (b *builder) processFilterNode(root *filterNode) (query, error) { if q.ValueType() != xpathResultType.Any {
b.flag |= filterFlag 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 { if err != nil {
return nil, err 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 { if err != nil {
return nil, err 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. // 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 var qyOutput query
switch root.FuncName { 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": case "starts-with":
arg1, err := b.processNode(root.Args[0]) arg1, err := b.processNode(root.Args[0], flagsEnum.None, props)
if err != nil { if err != nil {
return nil, err return nil, err
} }
arg2, err := b.processNode(root.Args[1]) arg2, err := b.processNode(root.Args[1], flagsEnum.None, props)
if err != nil { if err != nil {
return nil, err return nil, err
} }
qyOutput = &functionQuery{Input: b.firstInput, Func: startwithFunc(arg1, arg2)} qyOutput = &functionQuery{Func: startwithFunc(arg1, arg2)}
case "ends-with": case "ends-with":
arg1, err := b.processNode(root.Args[0]) arg1, err := b.processNode(root.Args[0], flagsEnum.None, props)
if err != nil { if err != nil {
return nil, err return nil, err
} }
arg2, err := b.processNode(root.Args[1]) arg2, err := b.processNode(root.Args[1], flagsEnum.None, props)
if err != nil { if err != nil {
return nil, err return nil, err
} }
qyOutput = &functionQuery{Input: b.firstInput, Func: endwithFunc(arg1, arg2)} qyOutput = &functionQuery{Func: endwithFunc(arg1, arg2)}
case "contains": case "contains":
arg1, err := b.processNode(root.Args[0]) arg1, err := b.processNode(root.Args[0], flagsEnum.None, props)
if err != nil { if err != nil {
return nil, err return nil, err
} }
arg2, err := b.processNode(root.Args[1]) arg2, err := b.processNode(root.Args[1], flagsEnum.None, props)
if err != nil { if err != nil {
return nil, err return nil, err
} }
qyOutput = &functionQuery{Input: b.firstInput, Func: containsFunc(arg1, arg2)} qyOutput = &functionQuery{Func: containsFunc(arg1, arg2)}
case "matches": case "matches":
//matches(string , pattern) //matches(string , pattern)
if len(root.Args) != 2 { if len(root.Args) != 2 {
@ -212,13 +383,19 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) {
arg1, arg2 query arg1, arg2 query
err error 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 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 return nil, err
} }
qyOutput = &functionQuery{Input: b.firstInput, Func: matchesFunc(arg1, arg2)} // Issue #92, testing the regular expression before.
if q, ok := arg2.(*constantQuery); ok {
if _, err = getRegexp(q.Val.(string)); err != nil {
return nil, fmt.Errorf("matches() got error. %v", err)
}
}
qyOutput = &functionQuery{Func: matchesFunc(arg1, arg2)}
case "substring": case "substring":
//substring( string , start [, length] ) //substring( string , start [, length] )
if len(root.Args) < 2 { if len(root.Args) < 2 {
@ -228,18 +405,18 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) {
arg1, arg2, arg3 query arg1, arg2, arg3 query
err error 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 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 return nil, err
} }
if len(root.Args) == 3 { 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 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": case "substring-before", "substring-after":
//substring-xxxx( haystack, needle ) //substring-xxxx( haystack, needle )
if len(root.Args) != 2 { if len(root.Args) != 2 {
@ -249,31 +426,30 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) {
arg1, arg2 query arg1, arg2 query
err error 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 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 return nil, err
} }
qyOutput = &functionQuery{ qyOutput = &functionQuery{
Input: b.firstInput, Func: substringIndFunc(arg1, arg2, root.FuncName == "substring-after"),
Func: substringIndFunc(arg1, arg2, root.FuncName == "substring-after"),
} }
case "string-length": case "string-length":
// string-length( [string] ) // string-length( [string] )
if len(root.Args) < 1 { if len(root.Args) < 1 {
return nil, errors.New("xpath: string-length function must have at least one parameter") 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 { if err != nil {
return nil, err return nil, err
} }
qyOutput = &functionQuery{Input: b.firstInput, Func: stringLengthFunc(arg1)} qyOutput = &functionQuery{Func: stringLengthFunc(arg1)}
case "normalize-space": case "normalize-space":
if len(root.Args) == 0 { if len(root.Args) == 0 {
return nil, errors.New("xpath: normalize-space function must have at least one parameter") 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 { if err != nil {
return nil, err return nil, err
} }
@ -287,16 +463,16 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) {
arg1, arg2, arg3 query arg1, arg2, arg3 query
err error 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 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 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 return nil, err
} }
qyOutput = &functionQuery{Input: b.firstInput, Func: replaceFunc(arg1, arg2, arg3)} qyOutput = &functionQuery{Func: replaceFunc(arg1, arg2, arg3)}
case "translate": case "translate":
//translate( string , string, string ) //translate( string , string, string )
if len(root.Args) != 3 { if len(root.Args) != 3 {
@ -306,21 +482,21 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) {
arg1, arg2, arg3 query arg1, arg2, arg3 query
err error 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 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 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 return nil, err
} }
qyOutput = &functionQuery{Input: b.firstInput, Func: translateFunc(arg1, arg2, arg3)} qyOutput = &functionQuery{Func: translateFunc(arg1, arg2, arg3)}
case "not": case "not":
if len(root.Args) == 0 { if len(root.Args) == 0 {
return nil, errors.New("xpath: not function must have at least one parameter") 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 { if err != nil {
return nil, err return nil, err
} }
@ -334,46 +510,46 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) {
err error err error
) )
if len(root.Args) == 1 { 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 { if err != nil {
return nil, err return nil, err
} }
} }
switch root.FuncName { switch root.FuncName {
case "name": case "name":
qyOutput = &functionQuery{Input: b.firstInput, Func: nameFunc(arg)} qyOutput = &functionQuery{Func: nameFunc(arg)}
case "local-name": case "local-name":
qyOutput = &functionQuery{Input: b.firstInput, Func: localNameFunc(arg)} qyOutput = &functionQuery{Func: localNameFunc(arg)}
case "namespace-uri": case "namespace-uri":
qyOutput = &functionQuery{Input: b.firstInput, Func: namespaceFunc(arg)} qyOutput = &functionQuery{Func: namespaceFunc(arg)}
} }
case "true", "false": case "true", "false":
val := root.FuncName == "true" val := root.FuncName == "true"
qyOutput = &functionQuery{ qyOutput = &functionQuery{
Input: b.firstInput,
Func: func(_ query, _ iterator) interface{} { Func: func(_ query, _ iterator) interface{} {
return val return val
}, },
} }
case "last": case "last":
switch typ := b.firstInput.(type) { //switch typ := b.firstInput.(type) {
case *groupQuery, *filterQuery: //case *groupQuery, *filterQuery:
// https://github.com/antchfx/xpath/issues/76 // https://github.com/antchfx/xpath/issues/76
// https://github.com/antchfx/xpath/issues/78 // https://github.com/antchfx/xpath/issues/78
qyOutput = &lastQuery{Input: typ} //qyOutput = &lastQuery{Input: typ}
default: //default:
qyOutput = &functionQuery{Input: b.firstInput, Func: lastFunc} qyOutput = &functionQuery{Func: lastFunc}
} //}
*props |= builderProps.HasLast
case "position": case "position":
qyOutput = &functionQuery{Input: b.firstInput, Func: positionFunc} qyOutput = &functionQuery{Func: positionFunc}
*props |= builderProps.HasPosition
case "boolean", "number", "string": case "boolean", "number", "string":
inp := b.firstInput var inp query
if len(root.Args) > 1 { if len(root.Args) > 1 {
return nil, fmt.Errorf("xpath: %s function must have at most one parameter", root.FuncName) return nil, fmt.Errorf("xpath: %s function must have at most one parameter", root.FuncName)
} }
if len(root.Args) == 1 { 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 { if err != nil {
return nil, err return nil, err
} }
@ -390,13 +566,10 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) {
} }
qyOutput = f qyOutput = f
case "count": case "count":
//if b.firstInput == nil {
// return nil, errors.New("xpath: expression must evaluate to node-set")
//}
if len(root.Args) == 0 { if len(root.Args) == 0 {
return nil, fmt.Errorf("xpath: count(node-sets) function must with have parameters node-sets") 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 { if err != nil {
return nil, err return nil, err
} }
@ -405,7 +578,7 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) {
if len(root.Args) == 0 { if len(root.Args) == 0 {
return nil, fmt.Errorf("xpath: sum(node-sets) function must with have parameters node-sets") 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 { if err != nil {
return nil, err return nil, err
} }
@ -414,7 +587,7 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) {
if len(root.Args) == 0 { if len(root.Args) == 0 {
return nil, fmt.Errorf("xpath: ceiling(node-sets) function must with have parameters node-sets") 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 { if err != nil {
return nil, err return nil, err
} }
@ -434,41 +607,65 @@ func (b *builder) processFunctionNode(root *functionNode) (query, error) {
} }
var args []query var args []query
for _, v := range root.Args { for _, v := range root.Args {
q, err := b.processNode(v) q, err := b.processNode(v, flagsEnum.None, props)
if err != nil { if err != nil {
return nil, err return nil, err
} }
args = append(args, q) args = append(args, q)
} }
qyOutput = &functionQuery{Input: b.firstInput, Func: concatFunc(args...)} qyOutput = &functionQuery{Func: concatFunc(args...)}
case "reverse": case "reverse":
if len(root.Args) == 0 { if len(root.Args) == 0 {
return nil, fmt.Errorf("xpath: reverse(node-sets) function must with have parameters node-sets") 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 { if err != nil {
return nil, err return nil, err
} }
qyOutput = &transformFunctionQuery{Input: argQuery, Func: reverseFunc} qyOutput = &transformFunctionQuery{Input: argQuery, Func: reverseFunc}
case "string-join":
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], flagsEnum.None, props)
if err != nil {
return nil, err
}
arg1, err := b.processNode(root.Args[1], flagsEnum.None, props)
if err != nil {
return nil, err
}
qyOutput = &functionQuery{Input: argQuery, Func: stringJoinFunc(arg1)}
default: default:
return nil, fmt.Errorf("not yet support this function %s()", root.FuncName) 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 return qyOutput, nil
} }
func (b *builder) processOperatorNode(root *operatorNode) (query, error) { func (b *builder) processOperator(root *operatorNode, props *builderProp) (query, error) {
left, err := b.processNode(root.Left) var (
leftProp builderProp
rightProp builderProp
)
left, err := b.processNode(root.Left, flagsEnum.None, &leftProp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
right, err := b.processNode(root.Right) right, err := b.processNode(root.Right, flagsEnum.None, &rightProp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
*props = leftProp | rightProp
var qyOutput query var qyOutput query
switch root.Op { switch root.Op {
case "+", "-", "*", "div", "mod": // Numeric operator case "+", "-", "*", "div", "mod": // Numeric operator
var exprFunc func(interface{}, interface{}) interface{} var exprFunc func(iterator, interface{}, interface{}) interface{}
switch root.Op { switch root.Op {
case "+": case "+":
exprFunc = plusFunc exprFunc = plusFunc
@ -506,41 +703,45 @@ func (b *builder) processOperatorNode(root *operatorNode) (query, error) {
} }
qyOutput = &booleanQuery{Left: left, Right: right, IsOr: isOr} qyOutput = &booleanQuery{Left: left, Right: right, IsOr: isOr}
case "|": case "|":
*props |= builderProps.NonFlat
qyOutput = &unionQuery{Left: left, Right: right} qyOutput = &unionQuery{Left: left, Right: right}
} }
return qyOutput, nil return qyOutput, nil
} }
func (b *builder) processNode(root node) (q query, err error) { func (b *builder) processNode(root node, flags flag, props *builderProp) (q query, err error) {
if b.depth = b.depth + 1; b.depth > 1024 { if b.parseDepth = b.parseDepth + 1; b.parseDepth > 1024 {
err = errors.New("the xpath expressions is too complex") err = errors.New("the xpath expressions is too complex")
return return
} }
*props = builderProps.None
switch root.Type() { switch root.Type() {
case nodeConstantOperand: case nodeConstantOperand:
n := root.(*operandNode) n := root.(*operandNode)
q = &constantQuery{Val: n.Val} q = &constantQuery{Val: n.Val}
case nodeRoot: case nodeRoot:
q = &contextQuery{Root: true} q = &absoluteQuery{}
case nodeAxis: case nodeAxis:
q, err = b.processAxisNode(root.(*axisNode)) q, err = b.processAxis(root.(*axisNode), flags, props)
b.firstInput = q b.firstInput = q
case nodeFilter: case nodeFilter:
q, err = b.processFilterNode(root.(*filterNode)) q, err = b.processFilter(root.(*filterNode), flags, props)
b.firstInput = q b.firstInput = q
case nodeFunction: case nodeFunction:
q, err = b.processFunctionNode(root.(*functionNode)) q, err = b.processFunction(root.(*functionNode), props)
case nodeOperator: case nodeOperator:
q, err = b.processOperatorNode(root.(*operatorNode)) q, err = b.processOperator(root.(*operatorNode), props)
case nodeGroup: case nodeGroup:
q, err = b.processNode(root.(*groupNode).Input) q, err = b.processNode(root.(*groupNode).Input, flagsEnum.None, props)
if err != nil { if err != nil {
return return
} }
q = &groupQuery{Input: q} q = &groupQuery{Input: q}
b.firstInput = q if b.firstInput == nil {
b.firstInput = q
}
} }
b.parseDepth--
return return
} }
@ -560,5 +761,6 @@ func build(expr string, namespaces map[string]string) (q query, err error) {
}() }()
root := parse(expr, namespaces) root := parse(expr, namespaces)
b := &builder{} b := &builder{}
return b.processNode(root) props := builderProps.None
return b.processNode(root, flagsEnum.None, &props)
} }

View File

@ -113,7 +113,7 @@ func asNumber(t iterator, o interface{}) float64 {
case query: case query:
node := typ.Select(t) node := typ.Select(t)
if node == nil { if node == nil {
return float64(0) return math.NaN()
} }
if v, err := strconv.ParseFloat(node.Value(), 64); err == nil { if v, err := strconv.ParseFloat(node.Value(), 64); err == nil {
return v return v
@ -614,3 +614,40 @@ func reverseFunc(q query, t iterator) func() NodeNavigator {
return node return node
} }
} }
// string-join is a XPath Node Set functions string-join(node-set, separator).
func stringJoinFunc(arg1 query) func(query, iterator) interface{} {
return func(q query, t iterator) interface{} {
var separator string
switch v := functionArgs(arg1).Evaluate(t).(type) {
case string:
separator = v
case query:
node := v.Select(t)
if node != nil {
separator = node.Value()
}
}
q = functionArgs(q)
test := predicate(q)
var parts []string
switch v := q.Evaluate(t).(type) {
case string:
return v
case query:
for node := v.Select(t); node != nil; node = v.Select(t) {
if test(node) {
parts = append(parts, node.Value())
}
}
}
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))
}

View File

@ -1,40 +1,11 @@
package xpath package xpath
import ( import (
"fmt"
"reflect"
"strconv" "strconv"
) )
// The XPath number operator function list. // 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 type logical func(iterator, string, interface{}, interface{}) bool
var logicalFuncs = [][]logical{ var logicalFuncs = [][]logical{
@ -228,91 +199,90 @@ func cmpBooleanBoolean(t iterator, op string, m, n interface{}) bool {
// eqFunc is an `=` operator. // eqFunc is an `=` operator.
func eqFunc(t iterator, m, n interface{}) interface{} { func eqFunc(t iterator, m, n interface{}) interface{} {
t1 := getValueType(m) t1 := getXPathType(m)
t2 := getValueType(n) t2 := getXPathType(n)
return logicalFuncs[t1][t2](t, "=", m, n) return logicalFuncs[t1][t2](t, "=", m, n)
} }
// gtFunc is an `>` operator. // gtFunc is an `>` operator.
func gtFunc(t iterator, m, n interface{}) interface{} { func gtFunc(t iterator, m, n interface{}) interface{} {
t1 := getValueType(m) t1 := getXPathType(m)
t2 := getValueType(n) t2 := getXPathType(n)
return logicalFuncs[t1][t2](t, ">", m, n) return logicalFuncs[t1][t2](t, ">", m, n)
} }
// geFunc is an `>=` operator. // geFunc is an `>=` operator.
func geFunc(t iterator, m, n interface{}) interface{} { func geFunc(t iterator, m, n interface{}) interface{} {
t1 := getValueType(m) t1 := getXPathType(m)
t2 := getValueType(n) t2 := getXPathType(n)
return logicalFuncs[t1][t2](t, ">=", m, n) return logicalFuncs[t1][t2](t, ">=", m, n)
} }
// ltFunc is an `<` operator. // ltFunc is an `<` operator.
func ltFunc(t iterator, m, n interface{}) interface{} { func ltFunc(t iterator, m, n interface{}) interface{} {
t1 := getValueType(m) t1 := getXPathType(m)
t2 := getValueType(n) t2 := getXPathType(n)
return logicalFuncs[t1][t2](t, "<", m, n) return logicalFuncs[t1][t2](t, "<", m, n)
} }
// leFunc is an `<=` operator. // leFunc is an `<=` operator.
func leFunc(t iterator, m, n interface{}) interface{} { func leFunc(t iterator, m, n interface{}) interface{} {
t1 := getValueType(m) t1 := getXPathType(m)
t2 := getValueType(n) t2 := getXPathType(n)
return logicalFuncs[t1][t2](t, "<=", m, n) return logicalFuncs[t1][t2](t, "<=", m, n)
} }
// neFunc is an `!=` operator. // neFunc is an `!=` operator.
func neFunc(t iterator, m, n interface{}) interface{} { func neFunc(t iterator, m, n interface{}) interface{} {
t1 := getValueType(m) t1 := getXPathType(m)
t2 := getValueType(n) t2 := getXPathType(n)
return logicalFuncs[t1][t2](t, "!=", m, n) return logicalFuncs[t1][t2](t, "!=", m, n)
} }
// orFunc is an `or` operator. // orFunc is an `or` operator.
var orFunc = func(t iterator, m, n interface{}) interface{} { var orFunc = func(t iterator, m, n interface{}) interface{} {
t1 := getValueType(m) t1 := getXPathType(m)
t2 := getValueType(n) t2 := getXPathType(n)
return logicalFuncs[t1][t2](t, "or", m, n) return logicalFuncs[t1][t2](t, "or", m, n)
} }
func numericExpr(m, n interface{}, cb func(float64, float64) float64) float64 { func numericExpr(t iterator, m, n interface{}, cb func(float64, float64) float64) float64 {
typ := reflect.TypeOf(float64(0)) a := asNumber(t, m)
a := reflect.ValueOf(m).Convert(typ) b := asNumber(t, n)
b := reflect.ValueOf(n).Convert(typ) return cb(a, b)
return cb(a.Float(), b.Float())
} }
// plusFunc is an `+` operator. // plusFunc is an `+` operator.
var plusFunc = func(m, n interface{}) interface{} { var plusFunc = func(t iterator, m, n interface{}) interface{} {
return numericExpr(m, n, func(a, b float64) float64 { return numericExpr(t, m, n, func(a, b float64) float64 {
return a + b return a + b
}) })
} }
// minusFunc is an `-` operator. // minusFunc is an `-` operator.
var minusFunc = func(m, n interface{}) interface{} { var minusFunc = func(t iterator, m, n interface{}) interface{} {
return numericExpr(m, n, func(a, b float64) float64 { return numericExpr(t, m, n, func(a, b float64) float64 {
return a - b return a - b
}) })
} }
// mulFunc is an `*` operator. // mulFunc is an `*` operator.
var mulFunc = func(m, n interface{}) interface{} { var mulFunc = func(t iterator, m, n interface{}) interface{} {
return numericExpr(m, n, func(a, b float64) float64 { return numericExpr(t, m, n, func(a, b float64) float64 {
return a * b return a * b
}) })
} }
// divFunc is an `DIV` operator. // divFunc is an `DIV` operator.
var divFunc = func(m, n interface{}) interface{} { var divFunc = func(t iterator, m, n interface{}) interface{} {
return numericExpr(m, n, func(a, b float64) float64 { return numericExpr(t, m, n, func(a, b float64) float64 {
return a / b return a / b
}) })
} }
// modFunc is an 'MOD' operator. // modFunc is an 'MOD' operator.
var modFunc = func(m, n interface{}) interface{} { var modFunc = func(t iterator, m, n interface{}) interface{} {
return numericExpr(m, n, func(a, b float64) float64 { return numericExpr(t, m, n, func(a, b float64) float64 {
return float64(int(a) % int(b)) return float64(int(a) % int(b))
}) })
} }

View File

@ -7,6 +7,44 @@ import (
"reflect" "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 { type iterator interface {
Current() NodeNavigator Current() NodeNavigator
} }
@ -20,12 +58,15 @@ type query interface {
Evaluate(iterator) interface{} Evaluate(iterator) interface{}
Clone() query 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. // nopQuery is an empty query that always return nil for any query.
type nopQuery struct { type nopQuery struct{}
query
}
func (nopQuery) Select(iterator) NodeNavigator { return nil } 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) 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. // contextQuery is returns current node on the iterator object query.
type contextQuery struct { type contextQuery struct {
count int count int
Root bool // Moving to root-level node in the current context iterator.
} }
func (c *contextQuery) Select(t iterator) (n NodeNavigator) { func (c *contextQuery) Select(t iterator) NodeNavigator {
if c.count == 0 { if c.count > 0 {
c.count++ return nil
n = t.Current().Copy()
if c.Root {
n.MoveToRoot()
}
} }
return n c.count++
return t.Current().Copy()
} }
func (c *contextQuery) Evaluate(iterator) interface{} { func (c *contextQuery) Evaluate(iterator) interface{} {
@ -56,12 +99,53 @@ func (c *contextQuery) Evaluate(iterator) interface{} {
} }
func (c *contextQuery) Clone() query { 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::*) // ancestorQuery is an XPath ancestor node query.(ancestor::*|ancestor-self::*)
type ancestorQuery struct { type ancestorQuery struct {
name string
iterator func() NodeNavigator iterator func() NodeNavigator
table map[uint64]bool
Self bool Self bool
Input query Input query
@ -69,6 +153,10 @@ type ancestorQuery struct {
} }
func (a *ancestorQuery) Select(t iterator) NodeNavigator { func (a *ancestorQuery) Select(t iterator) NodeNavigator {
if a.table == nil {
a.table = make(map[uint64]bool)
}
for { for {
if a.iterator == nil { if a.iterator == nil {
node := a.Input.Select(t) node := a.Input.Select(t)
@ -78,24 +166,27 @@ func (a *ancestorQuery) Select(t iterator) NodeNavigator {
first := true first := true
node = node.Copy() node = node.Copy()
a.iterator = func() NodeNavigator { a.iterator = func() NodeNavigator {
if first && a.Self { if first {
first = false first = false
if a.Predicate(node) { if a.Self && a.Predicate(node) {
return node return node
} }
} }
for node.MoveToParent() { for node.MoveToParent() {
if !a.Predicate(node) { if a.Predicate(node) {
continue return node
} }
return node
} }
return nil return nil
} }
} }
if node := a.iterator(); node != nil { for node := a.iterator(); node != nil; node = a.iterator() {
return node node_id := getHashCode(node.Copy())
if _, ok := a.table[node_id]; !ok {
a.table[node_id] = true
return node
}
} }
a.iterator = nil a.iterator = nil
} }
@ -112,11 +203,20 @@ func (a *ancestorQuery) Test(n NodeNavigator) bool {
} }
func (a *ancestorQuery) Clone() query { 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.(@*) // attributeQuery is an XPath attribute node query.(@*)
type attributeQuery struct { type attributeQuery struct {
name string
iterator func() NodeNavigator iterator func() NodeNavigator
Input query Input query
@ -162,11 +262,20 @@ func (a *attributeQuery) Test(n NodeNavigator) bool {
} }
func (a *attributeQuery) Clone() query { 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::*) // childQuery is an XPath child node query.(child::*)
type childQuery struct { type childQuery struct {
name string
posit int posit int
iterator func() NodeNavigator iterator func() NodeNavigator
@ -216,7 +325,15 @@ func (c *childQuery) Test(n NodeNavigator) bool {
} }
func (c *childQuery) Clone() query { 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. // position returns a position of current NodeNavigator.
@ -224,8 +341,75 @@ func (c *childQuery) position() int {
return c.posit 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::*) // descendantQuery is an XPath descendant node query.(descendant::* | descendant-or-self::*)
type descendantQuery struct { type descendantQuery struct {
name string
iterator func() NodeNavigator iterator func() NodeNavigator
posit int posit int
level int level int
@ -245,14 +429,11 @@ func (d *descendantQuery) Select(t iterator) NodeNavigator {
} }
node = node.Copy() node = node.Copy()
d.level = 0 d.level = 0
positmap := make(map[int]int)
first := true first := true
d.iterator = func() NodeNavigator { d.iterator = func() NodeNavigator {
if first && d.Self { if first {
first = false first = false
if d.Predicate(node) { if d.Self && d.Predicate(node) {
d.posit = 1
positmap[d.level] = 1
return node return node
} }
} }
@ -260,7 +441,6 @@ func (d *descendantQuery) Select(t iterator) NodeNavigator {
for { for {
if node.MoveToChild() { if node.MoveToChild() {
d.level = d.level + 1 d.level = d.level + 1
positmap[d.level] = 0
} else { } else {
for { for {
if d.level == 0 { if d.level == 0 {
@ -274,8 +454,6 @@ func (d *descendantQuery) Select(t iterator) NodeNavigator {
} }
} }
if d.Predicate(node) { if d.Predicate(node) {
positmap[d.level]++
d.posit = positmap[d.level]
return node return node
} }
} }
@ -283,6 +461,7 @@ func (d *descendantQuery) Select(t iterator) NodeNavigator {
} }
if node := d.iterator(); node != nil { if node := d.iterator(); node != nil {
d.posit++
return node return node
} }
d.iterator = nil d.iterator = nil
@ -309,7 +488,15 @@ func (d *descendantQuery) depth() int {
} }
func (d *descendantQuery) Clone() query { 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::*) // 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} 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 { func (f *followingQuery) position() int {
return f.posit return f.posit
} }
@ -471,6 +666,14 @@ func (p *precedingQuery) Clone() query {
return &precedingQuery{Input: p.Input.Clone(), Sibling: p.Sibling, Predicate: p.Predicate} 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 { func (p *precedingQuery) position() int {
return p.posit return p.posit
} }
@ -503,6 +706,14 @@ func (p *parentQuery) Clone() query {
return &parentQuery{Input: p.Input.Clone(), Predicate: p.Predicate} 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 { func (p *parentQuery) Test(n NodeNavigator) bool {
return p.Predicate(n) return p.Predicate(n)
} }
@ -539,12 +750,22 @@ func (s *selfQuery) Clone() query {
return &selfQuery{Input: s.Input.Clone(), Predicate: s.Predicate} 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. // filterQuery is an XPath query for predicate filter.
type filterQuery struct { type filterQuery struct {
Input query Input query
Predicate query Predicate query
posit int NoPosition bool
positmap map[int]int
posit int
positmap map[int]int
} }
func (f *filterQuery) do(t iterator) bool { 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()} 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 // functionQuery is an XPath function that returns a computed value for
// the Evaluate call of the current NodeNavigator node. Select call isn't // the Evaluate call of the current NodeNavigator node. Select call isn't
// applicable for functionQuery. // applicable for functionQuery.
@ -624,6 +853,14 @@ func (f *functionQuery) Clone() query {
return &functionQuery{Input: f.Input.Clone(), Func: f.Func} 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 // transformFunctionQuery diffs from functionQuery where the latter computes a scalar
// value (number,string,boolean) for the current NodeNavigator node while the former // value (number,string,boolean) for the current NodeNavigator node while the former
// (transformFunctionQuery) performs a mapping or transform of the current NodeNavigator // (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} 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. // constantQuery is an XPath constant operand.
type constantQuery struct { type constantQuery struct {
Val interface{} Val interface{}
@ -669,6 +914,14 @@ func (c *constantQuery) Clone() query {
return c 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 { type groupQuery struct {
posit int posit int
@ -692,6 +945,14 @@ func (g *groupQuery) Clone() query {
return &groupQuery{Input: g.Input.Clone()} 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 { func (g *groupQuery) position() int {
return g.posit 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} 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. // numericQuery is an XPath numeric operator expression.
type numericQuery struct { type numericQuery struct {
Left, Right query Left, Right query
Do func(interface{}, interface{}) interface{} Do func(iterator, interface{}, interface{}) interface{}
} }
func (n *numericQuery) Select(t iterator) NodeNavigator { 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{} { func (n *numericQuery) Evaluate(t iterator) interface{} {
m := n.Left.Evaluate(t) m := n.Left.Evaluate(t)
k := n.Right.Evaluate(t) k := n.Right.Evaluate(t)
return n.Do(m, k) return n.Do(t, m, k)
} }
func (n *numericQuery) Clone() query { func (n *numericQuery) Clone() query {
return &numericQuery{Left: n.Left.Clone(), Right: n.Right.Clone(), Do: n.Do} 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 { type booleanQuery struct {
IsOr bool IsOr bool
Left, Right query 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()} 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 { type unionQuery struct {
Left, Right query Left, Right query
iterator func() NodeNavigator iterator func() NodeNavigator
@ -894,6 +1179,14 @@ func (u *unionQuery) Clone() query {
return &unionQuery{Left: u.Left.Clone(), Right: u.Right.Clone()} 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 { type lastQuery struct {
buffer []NodeNavigator buffer []NodeNavigator
counted bool counted bool
@ -923,6 +1216,147 @@ func (q *lastQuery) Clone() query {
return &lastQuery{Input: q.Input.Clone()} 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 { func getHashCode(n NodeNavigator) uint64 {
var sb bytes.Buffer var sb bytes.Buffer
switch n.NodeType() { switch n.NodeType() {
@ -958,7 +1392,7 @@ func getHashCode(n NodeNavigator) uint64 {
} }
} }
h := fnv.New64a() h := fnv.New64a()
h.Write([]byte(sb.String())) h.Write(sb.Bytes())
return h.Sum64() return h.Sum64()
} }
@ -981,3 +1415,20 @@ func getNodeDepth(q query) int {
} }
return 0 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()))
}

View File

@ -84,13 +84,13 @@ func (t *NodeIterator) Current() NodeNavigator {
// MoveNext moves Navigator to the next match node. // MoveNext moves Navigator to the next match node.
func (t *NodeIterator) MoveNext() bool { func (t *NodeIterator) MoveNext() bool {
n := t.query.Select(t) n := t.query.Select(t)
if n != nil { if n == nil {
if !t.node.MoveTo(n) { return false
t.node = n.Copy()
}
return true
} }
return false if !t.node.MoveTo(n) {
t.node = n.Copy()
}
return true
} }
// Select selects a node set using the specified XPath expression. // Select selects a node set using the specified XPath expression.

4
vendor/golang.org/x/net/LICENSE generated vendored
View File

@ -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 Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are 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 copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the in the documentation and/or other materials provided with the
distribution. 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 contributors may be used to endorse or promote products derived from
this software without specific prior written permission. this software without specific prior written permission.

20
vendor/golang.org/x/net/html/doc.go generated vendored
View File

@ -99,14 +99,20 @@ Care should be taken when parsing and interpreting HTML, whether full documents
or fragments, within the framework of the HTML specification, especially with or fragments, within the framework of the HTML specification, especially with
regard to untrusted inputs. regard to untrusted inputs.
This package provides both a tokenizer and a parser. Only the parser constructs This package provides both a tokenizer and a parser, which implement the
a DOM according to the HTML specification, resolving malformed and misplaced tokenization, and tokenization and tree construction stages of the WHATWG HTML
tags where appropriate. The tokenizer simply tokenizes the HTML presented to it, parsing specification respectively. While the tokenizer parses and normalizes
and as such does not resolve issues that may exist in the processed HTML, individual HTML tokens, only the parser constructs the DOM tree from the
producing a literal interpretation of the input. tokenized HTML, as described in the tree construction stage of the
specification, dynamically modifying or extending the document's DOM tree.
If your use case requires semantically well-formed HTML, as defined by the If your use case requires semantically well-formed HTML documents, as defined by
WHATWG specifiction, the parser should be used rather than the tokenizer. the WHATWG specification, the parser should be used rather than the tokenizer.
In security contexts, if trust decisions are being made using the tokenized or
parsed content, the input must be re-serialized (for instance by using Render or
Token.String) in order for those trust decisions to hold, as the process of
tokenization or parsing may alter the content.
*/ */
package html // import "golang.org/x/net/html" package html // import "golang.org/x/net/html"

View File

@ -194,9 +194,8 @@ func render1(w writer, n *Node) error {
} }
} }
// Render any child nodes. // Render any child nodes
switch n.Data { if childTextNodesAreLiteral(n) {
case "iframe", "noembed", "noframes", "noscript", "plaintext", "script", "style", "xmp":
for c := n.FirstChild; c != nil; c = c.NextSibling { for c := n.FirstChild; c != nil; c = c.NextSibling {
if c.Type == TextNode { if c.Type == TextNode {
if _, err := w.WriteString(c.Data); err != nil { if _, err := w.WriteString(c.Data); err != nil {
@ -213,7 +212,7 @@ func render1(w writer, n *Node) error {
// last element in the file, with no closing tag. // last element in the file, with no closing tag.
return plaintextAbort return plaintextAbort
} }
default: } else {
for c := n.FirstChild; c != nil; c = c.NextSibling { for c := n.FirstChild; c != nil; c = c.NextSibling {
if err := render1(w, c); err != nil { if err := render1(w, c); err != nil {
return err return err
@ -231,6 +230,27 @@ func render1(w writer, n *Node) error {
return w.WriteByte('>') return w.WriteByte('>')
} }
func childTextNodesAreLiteral(n *Node) bool {
// Per WHATWG HTML 13.3, if the parent of the current node is a style,
// script, xmp, iframe, noembed, noframes, or plaintext element, and the
// current node is a text node, append the value of the node's data
// literally. The specification is not explicit about it, but we only
// enforce this if we are in the HTML namespace (i.e. when the namespace is
// "").
// NOTE: we also always include noscript elements, although the
// specification states that they should only be rendered as such if
// scripting is enabled for the node (which is not something we track).
if n.Namespace != "" {
return false
}
switch n.Data {
case "iframe", "noembed", "noframes", "noscript", "plaintext", "script", "style", "xmp":
return true
default:
return false
}
}
// writeQuoted writes s to w surrounded by quotes. Normally it will use double // writeQuoted writes s to w surrounded by quotes. Normally it will use double
// quotes, but if s contains a double quote, it will use single quotes. // quotes, but if s contains a double quote, it will use single quotes.
// It is used for writing the identifiers in a doctype declaration. // It is used for writing the identifiers in a doctype declaration.

View File

@ -910,10 +910,16 @@ func (z *Tokenizer) readTagAttrKey() {
return return
} }
switch c { switch c {
case ' ', '\n', '\r', '\t', '\f', '/': case '=':
z.pendingAttr[0].end = z.raw.end - 1 if z.pendingAttr[0].start+1 == z.raw.end {
return // WHATWG 13.2.5.32, if we see an equals sign before the attribute name
case '=', '>': // begins, we treat it as a character in the attribute name and continue.
continue
}
fallthrough
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.raw.end--
z.pendingAttr[0].end = z.raw.end z.pendingAttr[0].end = z.raw.end
return return
@ -932,6 +938,11 @@ func (z *Tokenizer) readTagAttrVal() {
if z.err != nil { if z.err != nil {
return 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 != '=' { if c != '=' {
z.raw.end-- z.raw.end--
return return

4
vendor/golang.org/x/text/LICENSE generated vendored
View File

@ -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 Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are 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 copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the in the documentation and/or other materials provided with the
distribution. 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 contributors may be used to endorse or promote products derived from
this software without specific prior written permission. this software without specific prior written permission.

View File

@ -790,226 +790,226 @@ const (
var coreTags = []language.CompactCoreInfo{ // 773 elements var coreTags = []language.CompactCoreInfo{ // 773 elements
// Entry 0 - 1F // Entry 0 - 1F
0x00000000, 0x01600000, 0x016000d2, 0x01600161, 0x00000000, 0x01600000, 0x016000d3, 0x01600162,
0x01c00000, 0x01c00052, 0x02100000, 0x02100080, 0x01c00000, 0x01c00052, 0x02100000, 0x02100081,
0x02700000, 0x0270006f, 0x03a00000, 0x03a00001, 0x02700000, 0x02700070, 0x03a00000, 0x03a00001,
0x03a00023, 0x03a00039, 0x03a00062, 0x03a00067, 0x03a00023, 0x03a00039, 0x03a00063, 0x03a00068,
0x03a0006b, 0x03a0006c, 0x03a0006d, 0x03a00097, 0x03a0006c, 0x03a0006d, 0x03a0006e, 0x03a00098,
0x03a0009b, 0x03a000a1, 0x03a000a8, 0x03a000ac, 0x03a0009c, 0x03a000a2, 0x03a000a9, 0x03a000ad,
0x03a000b0, 0x03a000b9, 0x03a000ba, 0x03a000c9, 0x03a000b1, 0x03a000ba, 0x03a000bb, 0x03a000ca,
0x03a000e1, 0x03a000ed, 0x03a000f3, 0x03a00108, 0x03a000e2, 0x03a000ee, 0x03a000f4, 0x03a00109,
// Entry 20 - 3F // Entry 20 - 3F
0x03a0010b, 0x03a00115, 0x03a00117, 0x03a0011c, 0x03a0010c, 0x03a00116, 0x03a00118, 0x03a0011d,
0x03a00120, 0x03a00128, 0x03a0015e, 0x04000000, 0x03a00121, 0x03a00129, 0x03a0015f, 0x04000000,
0x04300000, 0x04300099, 0x04400000, 0x0440012f, 0x04300000, 0x0430009a, 0x04400000, 0x04400130,
0x04800000, 0x0480006e, 0x05800000, 0x05820000, 0x04800000, 0x0480006f, 0x05800000, 0x05820000,
0x05820032, 0x0585a000, 0x0585a032, 0x05e00000, 0x05820032, 0x0585b000, 0x0585b032, 0x05e00000,
0x05e00052, 0x07100000, 0x07100047, 0x07500000, 0x05e00052, 0x07100000, 0x07100047, 0x07500000,
0x07500162, 0x07900000, 0x0790012f, 0x07e00000, 0x07500163, 0x07900000, 0x07900130, 0x07e00000,
0x07e00038, 0x08200000, 0x0a000000, 0x0a0000c3, 0x07e00038, 0x08200000, 0x0a000000, 0x0a0000c4,
// Entry 40 - 5F // Entry 40 - 5F
0x0a500000, 0x0a500035, 0x0a500099, 0x0a900000, 0x0a500000, 0x0a500035, 0x0a50009a, 0x0a900000,
0x0a900053, 0x0a900099, 0x0b200000, 0x0b200078, 0x0a900053, 0x0a90009a, 0x0b200000, 0x0b200079,
0x0b500000, 0x0b500099, 0x0b700000, 0x0b720000, 0x0b500000, 0x0b50009a, 0x0b700000, 0x0b720000,
0x0b720033, 0x0b75a000, 0x0b75a033, 0x0d700000, 0x0b720033, 0x0b75b000, 0x0b75b033, 0x0d700000,
0x0d700022, 0x0d70006e, 0x0d700078, 0x0d70009e, 0x0d700022, 0x0d70006f, 0x0d700079, 0x0d70009f,
0x0db00000, 0x0db00035, 0x0db00099, 0x0dc00000, 0x0db00000, 0x0db00035, 0x0db0009a, 0x0dc00000,
0x0dc00106, 0x0df00000, 0x0df00131, 0x0e500000, 0x0dc00107, 0x0df00000, 0x0df00132, 0x0e500000,
0x0e500135, 0x0e900000, 0x0e90009b, 0x0e90009c, 0x0e500136, 0x0e900000, 0x0e90009c, 0x0e90009d,
// Entry 60 - 7F // Entry 60 - 7F
0x0fa00000, 0x0fa0005e, 0x0fe00000, 0x0fe00106, 0x0fa00000, 0x0fa0005f, 0x0fe00000, 0x0fe00107,
0x10000000, 0x1000007b, 0x10100000, 0x10100063, 0x10000000, 0x1000007c, 0x10100000, 0x10100064,
0x10100082, 0x10800000, 0x108000a4, 0x10d00000, 0x10100083, 0x10800000, 0x108000a5, 0x10d00000,
0x10d0002e, 0x10d00036, 0x10d0004e, 0x10d00060, 0x10d0002e, 0x10d00036, 0x10d0004e, 0x10d00061,
0x10d0009e, 0x10d000b2, 0x10d000b7, 0x11700000, 0x10d0009f, 0x10d000b3, 0x10d000b8, 0x11700000,
0x117000d4, 0x11f00000, 0x11f00060, 0x12400000, 0x117000d5, 0x11f00000, 0x11f00061, 0x12400000,
0x12400052, 0x12800000, 0x12b00000, 0x12b00114, 0x12400052, 0x12800000, 0x12b00000, 0x12b00115,
0x12d00000, 0x12d00043, 0x12f00000, 0x12f000a4, 0x12d00000, 0x12d00043, 0x12f00000, 0x12f000a5,
// Entry 80 - 9F // Entry 80 - 9F
0x13000000, 0x13000080, 0x13000122, 0x13600000, 0x13000000, 0x13000081, 0x13000123, 0x13600000,
0x1360005d, 0x13600087, 0x13900000, 0x13900001, 0x1360005e, 0x13600088, 0x13900000, 0x13900001,
0x1390001a, 0x13900025, 0x13900026, 0x1390002d, 0x1390001a, 0x13900025, 0x13900026, 0x1390002d,
0x1390002e, 0x1390002f, 0x13900034, 0x13900036, 0x1390002e, 0x1390002f, 0x13900034, 0x13900036,
0x1390003a, 0x1390003d, 0x13900042, 0x13900046, 0x1390003a, 0x1390003d, 0x13900042, 0x13900046,
0x13900048, 0x13900049, 0x1390004a, 0x1390004e, 0x13900048, 0x13900049, 0x1390004a, 0x1390004e,
0x13900050, 0x13900052, 0x1390005c, 0x1390005d, 0x13900050, 0x13900052, 0x1390005d, 0x1390005e,
0x13900060, 0x13900061, 0x13900063, 0x13900064, 0x13900061, 0x13900062, 0x13900064, 0x13900065,
// Entry A0 - BF // Entry A0 - BF
0x1390006d, 0x13900072, 0x13900073, 0x13900074, 0x1390006e, 0x13900073, 0x13900074, 0x13900075,
0x13900075, 0x1390007b, 0x1390007c, 0x1390007f, 0x13900076, 0x1390007c, 0x1390007d, 0x13900080,
0x13900080, 0x13900081, 0x13900083, 0x1390008a, 0x13900081, 0x13900082, 0x13900084, 0x1390008b,
0x1390008c, 0x1390008d, 0x13900096, 0x13900097, 0x1390008d, 0x1390008e, 0x13900097, 0x13900098,
0x13900098, 0x13900099, 0x1390009a, 0x1390009f, 0x13900099, 0x1390009a, 0x1390009b, 0x139000a0,
0x139000a0, 0x139000a4, 0x139000a7, 0x139000a9, 0x139000a1, 0x139000a5, 0x139000a8, 0x139000aa,
0x139000ad, 0x139000b1, 0x139000b4, 0x139000b5, 0x139000ae, 0x139000b2, 0x139000b5, 0x139000b6,
0x139000bf, 0x139000c0, 0x139000c6, 0x139000c7, 0x139000c0, 0x139000c1, 0x139000c7, 0x139000c8,
// Entry C0 - DF // Entry C0 - DF
0x139000ca, 0x139000cb, 0x139000cc, 0x139000ce, 0x139000cb, 0x139000cc, 0x139000cd, 0x139000cf,
0x139000d0, 0x139000d2, 0x139000d5, 0x139000d6, 0x139000d1, 0x139000d3, 0x139000d6, 0x139000d7,
0x139000d9, 0x139000dd, 0x139000df, 0x139000e0, 0x139000da, 0x139000de, 0x139000e0, 0x139000e1,
0x139000e6, 0x139000e7, 0x139000e8, 0x139000eb, 0x139000e7, 0x139000e8, 0x139000e9, 0x139000ec,
0x139000ec, 0x139000f0, 0x13900107, 0x13900109, 0x139000ed, 0x139000f1, 0x13900108, 0x1390010a,
0x1390010a, 0x1390010b, 0x1390010c, 0x1390010d, 0x1390010b, 0x1390010c, 0x1390010d, 0x1390010e,
0x1390010e, 0x1390010f, 0x13900112, 0x13900117, 0x1390010f, 0x13900110, 0x13900113, 0x13900118,
0x1390011b, 0x1390011d, 0x1390011f, 0x13900125, 0x1390011c, 0x1390011e, 0x13900120, 0x13900126,
// Entry E0 - FF // Entry E0 - FF
0x13900129, 0x1390012c, 0x1390012d, 0x1390012f, 0x1390012a, 0x1390012d, 0x1390012e, 0x13900130,
0x13900131, 0x13900133, 0x13900135, 0x13900139, 0x13900132, 0x13900134, 0x13900136, 0x1390013a,
0x1390013c, 0x1390013d, 0x1390013f, 0x13900142, 0x1390013d, 0x1390013e, 0x13900140, 0x13900143,
0x13900161, 0x13900162, 0x13900164, 0x13c00000, 0x13900162, 0x13900163, 0x13900165, 0x13c00000,
0x13c00001, 0x13e00000, 0x13e0001f, 0x13e0002c, 0x13c00001, 0x13e00000, 0x13e0001f, 0x13e0002c,
0x13e0003f, 0x13e00041, 0x13e00048, 0x13e00051, 0x13e0003f, 0x13e00041, 0x13e00048, 0x13e00051,
0x13e00054, 0x13e00056, 0x13e00059, 0x13e00065, 0x13e00054, 0x13e00057, 0x13e0005a, 0x13e00066,
0x13e00068, 0x13e00069, 0x13e0006e, 0x13e00086, 0x13e00069, 0x13e0006a, 0x13e0006f, 0x13e00087,
// Entry 100 - 11F // Entry 100 - 11F
0x13e00089, 0x13e0008f, 0x13e00094, 0x13e000cf, 0x13e0008a, 0x13e00090, 0x13e00095, 0x13e000d0,
0x13e000d8, 0x13e000e2, 0x13e000e4, 0x13e000e7, 0x13e000d9, 0x13e000e3, 0x13e000e5, 0x13e000e8,
0x13e000ec, 0x13e000f1, 0x13e0011a, 0x13e00135, 0x13e000ed, 0x13e000f2, 0x13e0011b, 0x13e00136,
0x13e00136, 0x13e0013b, 0x14000000, 0x1400006a, 0x13e00137, 0x13e0013c, 0x14000000, 0x1400006b,
0x14500000, 0x1450006e, 0x14600000, 0x14600052, 0x14500000, 0x1450006f, 0x14600000, 0x14600052,
0x14800000, 0x14800024, 0x1480009c, 0x14e00000, 0x14800000, 0x14800024, 0x1480009d, 0x14e00000,
0x14e00052, 0x14e00084, 0x14e000c9, 0x14e00114, 0x14e00052, 0x14e00085, 0x14e000ca, 0x14e00115,
0x15100000, 0x15100072, 0x15300000, 0x153000e7, 0x15100000, 0x15100073, 0x15300000, 0x153000e8,
// Entry 120 - 13F // Entry 120 - 13F
0x15800000, 0x15800063, 0x15800076, 0x15e00000, 0x15800000, 0x15800064, 0x15800077, 0x15e00000,
0x15e00036, 0x15e00037, 0x15e0003a, 0x15e0003b, 0x15e00036, 0x15e00037, 0x15e0003a, 0x15e0003b,
0x15e0003c, 0x15e00049, 0x15e0004b, 0x15e0004c, 0x15e0003c, 0x15e00049, 0x15e0004b, 0x15e0004c,
0x15e0004d, 0x15e0004e, 0x15e0004f, 0x15e00052, 0x15e0004d, 0x15e0004e, 0x15e0004f, 0x15e00052,
0x15e00062, 0x15e00067, 0x15e00078, 0x15e0007a, 0x15e00063, 0x15e00068, 0x15e00079, 0x15e0007b,
0x15e0007e, 0x15e00084, 0x15e00085, 0x15e00086, 0x15e0007f, 0x15e00085, 0x15e00086, 0x15e00087,
0x15e00091, 0x15e000a8, 0x15e000b7, 0x15e000ba, 0x15e00092, 0x15e000a9, 0x15e000b8, 0x15e000bb,
0x15e000bb, 0x15e000be, 0x15e000bf, 0x15e000c3, 0x15e000bc, 0x15e000bf, 0x15e000c0, 0x15e000c4,
// Entry 140 - 15F // Entry 140 - 15F
0x15e000c8, 0x15e000c9, 0x15e000cc, 0x15e000d3, 0x15e000c9, 0x15e000ca, 0x15e000cd, 0x15e000d4,
0x15e000d4, 0x15e000e5, 0x15e000ea, 0x15e00102, 0x15e000d5, 0x15e000e6, 0x15e000eb, 0x15e00103,
0x15e00107, 0x15e0010a, 0x15e00114, 0x15e0011c, 0x15e00108, 0x15e0010b, 0x15e00115, 0x15e0011d,
0x15e00120, 0x15e00122, 0x15e00128, 0x15e0013f, 0x15e00121, 0x15e00123, 0x15e00129, 0x15e00140,
0x15e00140, 0x15e0015f, 0x16900000, 0x1690009e, 0x15e00141, 0x15e00160, 0x16900000, 0x1690009f,
0x16d00000, 0x16d000d9, 0x16e00000, 0x16e00096, 0x16d00000, 0x16d000da, 0x16e00000, 0x16e00097,
0x17e00000, 0x17e0007b, 0x19000000, 0x1900006e, 0x17e00000, 0x17e0007c, 0x19000000, 0x1900006f,
0x1a300000, 0x1a30004e, 0x1a300078, 0x1a3000b2, 0x1a300000, 0x1a30004e, 0x1a300079, 0x1a3000b3,
// Entry 160 - 17F // Entry 160 - 17F
0x1a400000, 0x1a400099, 0x1a900000, 0x1ab00000, 0x1a400000, 0x1a40009a, 0x1a900000, 0x1ab00000,
0x1ab000a4, 0x1ac00000, 0x1ac00098, 0x1b400000, 0x1ab000a5, 0x1ac00000, 0x1ac00099, 0x1b400000,
0x1b400080, 0x1b4000d4, 0x1b4000d6, 0x1b800000, 0x1b400081, 0x1b4000d5, 0x1b4000d7, 0x1b800000,
0x1b800135, 0x1bc00000, 0x1bc00097, 0x1be00000, 0x1b800136, 0x1bc00000, 0x1bc00098, 0x1be00000,
0x1be00099, 0x1d100000, 0x1d100033, 0x1d100090, 0x1be0009a, 0x1d100000, 0x1d100033, 0x1d100091,
0x1d200000, 0x1d200060, 0x1d500000, 0x1d500092, 0x1d200000, 0x1d200061, 0x1d500000, 0x1d500093,
0x1d700000, 0x1d700028, 0x1e100000, 0x1e100095, 0x1d700000, 0x1d700028, 0x1e100000, 0x1e100096,
0x1e700000, 0x1e7000d6, 0x1ea00000, 0x1ea00053, 0x1e700000, 0x1e7000d7, 0x1ea00000, 0x1ea00053,
// Entry 180 - 19F // Entry 180 - 19F
0x1f300000, 0x1f500000, 0x1f800000, 0x1f80009d, 0x1f300000, 0x1f500000, 0x1f800000, 0x1f80009e,
0x1f900000, 0x1f90004e, 0x1f90009e, 0x1f900113, 0x1f900000, 0x1f90004e, 0x1f90009f, 0x1f900114,
0x1f900138, 0x1fa00000, 0x1fb00000, 0x20000000, 0x1f900139, 0x1fa00000, 0x1fb00000, 0x20000000,
0x200000a2, 0x20300000, 0x20700000, 0x20700052, 0x200000a3, 0x20300000, 0x20700000, 0x20700052,
0x20800000, 0x20a00000, 0x20a0012f, 0x20e00000, 0x20800000, 0x20a00000, 0x20a00130, 0x20e00000,
0x20f00000, 0x21000000, 0x2100007d, 0x21200000, 0x20f00000, 0x21000000, 0x2100007e, 0x21200000,
0x21200067, 0x21600000, 0x21700000, 0x217000a4, 0x21200068, 0x21600000, 0x21700000, 0x217000a5,
0x21f00000, 0x22300000, 0x2230012f, 0x22700000, 0x21f00000, 0x22300000, 0x22300130, 0x22700000,
// Entry 1A0 - 1BF // Entry 1A0 - 1BF
0x2270005a, 0x23400000, 0x234000c3, 0x23900000, 0x2270005b, 0x23400000, 0x234000c4, 0x23900000,
0x239000a4, 0x24200000, 0x242000ae, 0x24400000, 0x239000a5, 0x24200000, 0x242000af, 0x24400000,
0x24400052, 0x24500000, 0x24500082, 0x24600000, 0x24400052, 0x24500000, 0x24500083, 0x24600000,
0x246000a4, 0x24a00000, 0x24a000a6, 0x25100000, 0x246000a5, 0x24a00000, 0x24a000a7, 0x25100000,
0x25100099, 0x25400000, 0x254000aa, 0x254000ab, 0x2510009a, 0x25400000, 0x254000ab, 0x254000ac,
0x25600000, 0x25600099, 0x26a00000, 0x26a00099, 0x25600000, 0x2560009a, 0x26a00000, 0x26a0009a,
0x26b00000, 0x26b0012f, 0x26d00000, 0x26d00052, 0x26b00000, 0x26b00130, 0x26d00000, 0x26d00052,
0x26e00000, 0x26e00060, 0x27400000, 0x28100000, 0x26e00000, 0x26e00061, 0x27400000, 0x28100000,
// Entry 1C0 - 1DF // Entry 1C0 - 1DF
0x2810007b, 0x28a00000, 0x28a000a5, 0x29100000, 0x2810007c, 0x28a00000, 0x28a000a6, 0x29100000,
0x2910012f, 0x29500000, 0x295000b7, 0x2a300000, 0x29100130, 0x29500000, 0x295000b8, 0x2a300000,
0x2a300131, 0x2af00000, 0x2af00135, 0x2b500000, 0x2a300132, 0x2af00000, 0x2af00136, 0x2b500000,
0x2b50002a, 0x2b50004b, 0x2b50004c, 0x2b50004d, 0x2b50002a, 0x2b50004b, 0x2b50004c, 0x2b50004d,
0x2b800000, 0x2b8000af, 0x2bf00000, 0x2bf0009b, 0x2b800000, 0x2b8000b0, 0x2bf00000, 0x2bf0009c,
0x2bf0009c, 0x2c000000, 0x2c0000b6, 0x2c200000, 0x2bf0009d, 0x2c000000, 0x2c0000b7, 0x2c200000,
0x2c20004b, 0x2c400000, 0x2c4000a4, 0x2c500000, 0x2c20004b, 0x2c400000, 0x2c4000a5, 0x2c500000,
0x2c5000a4, 0x2c700000, 0x2c7000b8, 0x2d100000, 0x2c5000a5, 0x2c700000, 0x2c7000b9, 0x2d100000,
// Entry 1E0 - 1FF // Entry 1E0 - 1FF
0x2d1000a4, 0x2d10012f, 0x2e900000, 0x2e9000a4, 0x2d1000a5, 0x2d100130, 0x2e900000, 0x2e9000a5,
0x2ed00000, 0x2ed000cc, 0x2f100000, 0x2f1000bf, 0x2ed00000, 0x2ed000cd, 0x2f100000, 0x2f1000c0,
0x2f200000, 0x2f2000d1, 0x2f400000, 0x2f400052, 0x2f200000, 0x2f2000d2, 0x2f400000, 0x2f400052,
0x2ff00000, 0x2ff000c2, 0x30400000, 0x30400099, 0x2ff00000, 0x2ff000c3, 0x30400000, 0x3040009a,
0x30b00000, 0x30b000c5, 0x31000000, 0x31b00000, 0x30b00000, 0x30b000c6, 0x31000000, 0x31b00000,
0x31b00099, 0x31f00000, 0x31f0003e, 0x31f000d0, 0x31b0009a, 0x31f00000, 0x31f0003e, 0x31f000d1,
0x31f0010d, 0x32000000, 0x320000cb, 0x32500000, 0x31f0010e, 0x32000000, 0x320000cc, 0x32500000,
0x32500052, 0x33100000, 0x331000c4, 0x33a00000, 0x32500052, 0x33100000, 0x331000c5, 0x33a00000,
// Entry 200 - 21F // Entry 200 - 21F
0x33a0009c, 0x34100000, 0x34500000, 0x345000d2, 0x33a0009d, 0x34100000, 0x34500000, 0x345000d3,
0x34700000, 0x347000da, 0x34700110, 0x34e00000, 0x34700000, 0x347000db, 0x34700111, 0x34e00000,
0x34e00164, 0x35000000, 0x35000060, 0x350000d9, 0x34e00165, 0x35000000, 0x35000061, 0x350000da,
0x35100000, 0x35100099, 0x351000db, 0x36700000, 0x35100000, 0x3510009a, 0x351000dc, 0x36700000,
0x36700030, 0x36700036, 0x36700040, 0x3670005b, 0x36700030, 0x36700036, 0x36700040, 0x3670005c,
0x367000d9, 0x36700116, 0x3670011b, 0x36800000, 0x367000da, 0x36700117, 0x3670011c, 0x36800000,
0x36800052, 0x36a00000, 0x36a000da, 0x36c00000, 0x36800052, 0x36a00000, 0x36a000db, 0x36c00000,
0x36c00052, 0x36f00000, 0x37500000, 0x37600000, 0x36c00052, 0x36f00000, 0x37500000, 0x37600000,
// Entry 220 - 23F // Entry 220 - 23F
0x37a00000, 0x38000000, 0x38000117, 0x38700000, 0x37a00000, 0x38000000, 0x38000118, 0x38700000,
0x38900000, 0x38900131, 0x39000000, 0x3900006f, 0x38900000, 0x38900132, 0x39000000, 0x39000070,
0x390000a4, 0x39500000, 0x39500099, 0x39800000, 0x390000a5, 0x39500000, 0x3950009a, 0x39800000,
0x3980007d, 0x39800106, 0x39d00000, 0x39d05000, 0x3980007e, 0x39800107, 0x39d00000, 0x39d05000,
0x39d050e8, 0x39d36000, 0x39d36099, 0x3a100000, 0x39d050e9, 0x39d36000, 0x39d3609a, 0x3a100000,
0x3b300000, 0x3b3000e9, 0x3bd00000, 0x3bd00001, 0x3b300000, 0x3b3000ea, 0x3bd00000, 0x3bd00001,
0x3be00000, 0x3be00024, 0x3c000000, 0x3c00002a, 0x3be00000, 0x3be00024, 0x3c000000, 0x3c00002a,
0x3c000041, 0x3c00004e, 0x3c00005a, 0x3c000086, 0x3c000041, 0x3c00004e, 0x3c00005b, 0x3c000087,
// Entry 240 - 25F // Entry 240 - 25F
0x3c00008b, 0x3c0000b7, 0x3c0000c6, 0x3c0000d1, 0x3c00008c, 0x3c0000b8, 0x3c0000c7, 0x3c0000d2,
0x3c0000ee, 0x3c000118, 0x3c000126, 0x3c400000, 0x3c0000ef, 0x3c000119, 0x3c000127, 0x3c400000,
0x3c40003f, 0x3c400069, 0x3c4000e4, 0x3d400000, 0x3c40003f, 0x3c40006a, 0x3c4000e5, 0x3d400000,
0x3d40004e, 0x3d900000, 0x3d90003a, 0x3dc00000, 0x3d40004e, 0x3d900000, 0x3d90003a, 0x3dc00000,
0x3dc000bc, 0x3dc00104, 0x3de00000, 0x3de0012f, 0x3dc000bd, 0x3dc00105, 0x3de00000, 0x3de00130,
0x3e200000, 0x3e200047, 0x3e2000a5, 0x3e2000ae, 0x3e200000, 0x3e200047, 0x3e2000a6, 0x3e2000af,
0x3e2000bc, 0x3e200106, 0x3e200130, 0x3e500000, 0x3e2000bd, 0x3e200107, 0x3e200131, 0x3e500000,
0x3e500107, 0x3e600000, 0x3e60012f, 0x3eb00000, 0x3e500108, 0x3e600000, 0x3e600130, 0x3eb00000,
// Entry 260 - 27F // Entry 260 - 27F
0x3eb00106, 0x3ec00000, 0x3ec000a4, 0x3f300000, 0x3eb00107, 0x3ec00000, 0x3ec000a5, 0x3f300000,
0x3f30012f, 0x3fa00000, 0x3fa000e8, 0x3fc00000, 0x3f300130, 0x3fa00000, 0x3fa000e9, 0x3fc00000,
0x3fd00000, 0x3fd00072, 0x3fd000da, 0x3fd0010c, 0x3fd00000, 0x3fd00073, 0x3fd000db, 0x3fd0010d,
0x3ff00000, 0x3ff000d1, 0x40100000, 0x401000c3, 0x3ff00000, 0x3ff000d2, 0x40100000, 0x401000c4,
0x40200000, 0x4020004c, 0x40700000, 0x40800000, 0x40200000, 0x4020004c, 0x40700000, 0x40800000,
0x4085a000, 0x4085a0ba, 0x408e8000, 0x408e80ba, 0x4085b000, 0x4085b0bb, 0x408eb000, 0x408eb0bb,
0x40c00000, 0x40c000b3, 0x41200000, 0x41200111, 0x40c00000, 0x40c000b4, 0x41200000, 0x41200112,
0x41600000, 0x4160010f, 0x41c00000, 0x41d00000, 0x41600000, 0x41600110, 0x41c00000, 0x41d00000,
// Entry 280 - 29F // Entry 280 - 29F
0x41e00000, 0x41f00000, 0x41f00072, 0x42200000, 0x41e00000, 0x41f00000, 0x41f00073, 0x42200000,
0x42300000, 0x42300164, 0x42900000, 0x42900062, 0x42300000, 0x42300165, 0x42900000, 0x42900063,
0x4290006f, 0x429000a4, 0x42900115, 0x43100000, 0x42900070, 0x429000a5, 0x42900116, 0x43100000,
0x43100027, 0x431000c2, 0x4310014d, 0x43200000, 0x43100027, 0x431000c3, 0x4310014e, 0x43200000,
0x43220000, 0x43220033, 0x432200bd, 0x43220105, 0x43220000, 0x43220033, 0x432200be, 0x43220106,
0x4322014d, 0x4325a000, 0x4325a033, 0x4325a0bd, 0x4322014e, 0x4325b000, 0x4325b033, 0x4325b0be,
0x4325a105, 0x4325a14d, 0x43700000, 0x43a00000, 0x4325b106, 0x4325b14e, 0x43700000, 0x43a00000,
0x43b00000, 0x44400000, 0x44400031, 0x44400072, 0x43b00000, 0x44400000, 0x44400031, 0x44400073,
// Entry 2A0 - 2BF // Entry 2A0 - 2BF
0x4440010c, 0x44500000, 0x4450004b, 0x445000a4, 0x4440010d, 0x44500000, 0x4450004b, 0x445000a5,
0x4450012f, 0x44500131, 0x44e00000, 0x45000000, 0x44500130, 0x44500132, 0x44e00000, 0x45000000,
0x45000099, 0x450000b3, 0x450000d0, 0x4500010d, 0x4500009a, 0x450000b4, 0x450000d1, 0x4500010e,
0x46100000, 0x46100099, 0x46400000, 0x464000a4, 0x46100000, 0x4610009a, 0x46400000, 0x464000a5,
0x46400131, 0x46700000, 0x46700124, 0x46b00000, 0x46400132, 0x46700000, 0x46700125, 0x46b00000,
0x46b00123, 0x46f00000, 0x46f0006d, 0x46f0006f, 0x46b00124, 0x46f00000, 0x46f0006e, 0x46f00070,
0x47100000, 0x47600000, 0x47600127, 0x47a00000, 0x47100000, 0x47600000, 0x47600128, 0x47a00000,
0x48000000, 0x48200000, 0x48200129, 0x48a00000, 0x48000000, 0x48200000, 0x4820012a, 0x48a00000,
// Entry 2C0 - 2DF // Entry 2C0 - 2DF
0x48a0005d, 0x48a0012b, 0x48e00000, 0x49400000, 0x48a0005e, 0x48a0012c, 0x48e00000, 0x49400000,
0x49400106, 0x4a400000, 0x4a4000d4, 0x4a900000, 0x49400107, 0x4a400000, 0x4a4000d5, 0x4a900000,
0x4a9000ba, 0x4ac00000, 0x4ac00053, 0x4ae00000, 0x4a9000bb, 0x4ac00000, 0x4ac00053, 0x4ae00000,
0x4ae00130, 0x4b400000, 0x4b400099, 0x4b4000e8, 0x4ae00131, 0x4b400000, 0x4b40009a, 0x4b4000e9,
0x4bc00000, 0x4bc05000, 0x4bc05024, 0x4bc20000, 0x4bc00000, 0x4bc05000, 0x4bc05024, 0x4bc20000,
0x4bc20137, 0x4bc5a000, 0x4bc5a137, 0x4be00000, 0x4bc20138, 0x4bc5b000, 0x4bc5b138, 0x4be00000,
0x4be5a000, 0x4be5a0b4, 0x4bef1000, 0x4bef10b4, 0x4be5b000, 0x4be5b0b5, 0x4bef4000, 0x4bef40b5,
0x4c000000, 0x4c300000, 0x4c30013e, 0x4c900000, 0x4c000000, 0x4c300000, 0x4c30013f, 0x4c900000,
// Entry 2E0 - 2FF // Entry 2E0 - 2FF
0x4c900001, 0x4cc00000, 0x4cc0012f, 0x4ce00000, 0x4c900001, 0x4cc00000, 0x4cc00130, 0x4ce00000,
0x4cf00000, 0x4cf0004e, 0x4e500000, 0x4e500114, 0x4cf00000, 0x4cf0004e, 0x4e500000, 0x4e500115,
0x4f200000, 0x4fb00000, 0x4fb00131, 0x50900000, 0x4f200000, 0x4fb00000, 0x4fb00132, 0x50900000,
0x50900052, 0x51200000, 0x51200001, 0x51800000, 0x50900052, 0x51200000, 0x51200001, 0x51800000,
0x5180003b, 0x518000d6, 0x51f00000, 0x51f3b000, 0x5180003b, 0x518000d7, 0x51f00000, 0x51f3b000,
0x51f3b053, 0x51f3c000, 0x51f3c08d, 0x52800000, 0x51f3b053, 0x51f3c000, 0x51f3c08e, 0x52800000,
0x528000ba, 0x52900000, 0x5293b000, 0x5293b053, 0x528000bb, 0x52900000, 0x5293b000, 0x5293b053,
0x5293b08d, 0x5293b0c6, 0x5293b10d, 0x5293c000, 0x5293b08e, 0x5293b0c7, 0x5293b10e, 0x5293c000,
// Entry 300 - 31F // Entry 300 - 31F
0x5293c08d, 0x5293c0c6, 0x5293c12e, 0x52f00000, 0x5293c08e, 0x5293c0c7, 0x5293c12f, 0x52f00000,
0x52f00161, 0x52f00162,
} // Size: 3116 bytes } // Size: 3116 bytes
const specialTagsStr string = "ca-ES-valencia en-US-u-va-posix" const specialTagsStr string = "ca-ES-valencia en-US-u-va-posix"
// Total table size 3147 bytes (3KiB); checksum: 6772C83C // Total table size 3147 bytes (3KiB); checksum: 5A8FFFA5

File diff suppressed because it is too large Load Diff

View File

@ -434,7 +434,7 @@ func newMatcher(supported []Tag, options []MatchOption) *matcher {
// (their canonicalization simply substitutes a different language code, but // (their canonicalization simply substitutes a different language code, but
// nothing else), the match confidence is Exact, otherwise it is High. // nothing else), the match confidence is Exact, otherwise it is High.
for i, lm := range language.AliasMap { for i, lm := range language.AliasMap {
// If deprecated codes match and there is no fiddling with the script or // If deprecated codes match and there is no fiddling with the script
// or region, we consider it an exact match. // or region, we consider it an exact match.
conf := Exact conf := Exact
if language.AliasTypes[i] != language.Macro { if language.AliasTypes[i] != language.Macro {

View File

@ -23,31 +23,31 @@ const (
_419 = 31 _419 = 31
_BR = 65 _BR = 65
_CA = 73 _CA = 73
_ES = 110 _ES = 111
_GB = 123 _GB = 124
_MD = 188 _MD = 189
_PT = 238 _PT = 239
_UK = 306 _UK = 307
_US = 309 _US = 310
_ZZ = 357 _ZZ = 358
_XA = 323 _XA = 324
_XC = 325 _XC = 326
_XK = 333 _XK = 334
) )
const ( const (
_Latn = 90 _Latn = 91
_Hani = 57 _Hani = 57
_Hans = 59 _Hans = 59
_Hant = 60 _Hant = 60
_Qaaa = 147 _Qaaa = 149
_Qaai = 155 _Qaai = 157
_Qabx = 196 _Qabx = 198
_Zinh = 252 _Zinh = 255
_Zyyy = 257 _Zyyy = 260
_Zzzz = 258 _Zzzz = 261
) )
var regionToGroups = []uint8{ // 358 elements var regionToGroups = []uint8{ // 359 elements
// Entry 0 - 3F // Entry 0 - 3F
0x00, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x04,
0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x00,
@ -60,51 +60,51 @@ var regionToGroups = []uint8{ // 358 elements
// Entry 40 - 7F // Entry 40 - 7F
0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04,
0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x00, 0x08,
0x00, 0x04, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00,
// Entry 80 - BF
0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00,
0x00, 0x04, 0x01, 0x00, 0x04, 0x02, 0x00, 0x04,
0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x08, 0x08, 0x00, 0x00, 0x00, 0x04, 0x00,
// Entry C0 - FF
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01,
0x04, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x04,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x04, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x00,
0x08, 0x00, 0x04, 0x00, 0x00, 0x08, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04,
// Entry 80 - BF
0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00,
0x00, 0x00, 0x04, 0x01, 0x00, 0x04, 0x02, 0x00,
0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00,
0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x00, 0x04,
// Entry C0 - FF
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
0x01, 0x04, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00,
0x00, 0x00, 0x00, 0x04, 0x00, 0x05, 0x00, 0x00,
0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Entry 100 - 13F // Entry 100 - 13F
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x00, 0x05, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x05, 0x04,
0x00, 0x04, 0x00, 0x04, 0x04, 0x05, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x04, 0x05, 0x00,
// Entry 140 - 17F // Entry 140 - 17F
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
} // Size: 382 bytes } // Size: 383 bytes
var paradigmLocales = [][3]uint16{ // 3 elements var paradigmLocales = [][3]uint16{ // 3 elements
0: [3]uint16{0x139, 0x0, 0x7b}, 0: [3]uint16{0x139, 0x0, 0x7c},
1: [3]uint16{0x13e, 0x0, 0x1f}, 1: [3]uint16{0x13e, 0x0, 0x1f},
2: [3]uint16{0x3c0, 0x41, 0xee}, 2: [3]uint16{0x3c0, 0x41, 0xef},
} // Size: 42 bytes } // Size: 42 bytes
type mutualIntelligibility struct { type mutualIntelligibility struct {
@ -249,30 +249,30 @@ var matchLang = []mutualIntelligibility{ // 113 elements
// matchScript holds pairs of scriptIDs where readers of one script // matchScript holds pairs of scriptIDs where readers of one script
// can typically also read the other. Each is associated with a confidence. // can typically also read the other. Each is associated with a confidence.
var matchScript = []scriptIntelligibility{ // 26 elements var matchScript = []scriptIntelligibility{ // 26 elements
0: {wantLang: 0x432, haveLang: 0x432, wantScript: 0x5a, haveScript: 0x20, distance: 0x5}, 0: {wantLang: 0x432, haveLang: 0x432, wantScript: 0x5b, haveScript: 0x20, distance: 0x5},
1: {wantLang: 0x432, haveLang: 0x432, wantScript: 0x20, haveScript: 0x5a, distance: 0x5}, 1: {wantLang: 0x432, haveLang: 0x432, wantScript: 0x20, haveScript: 0x5b, distance: 0x5},
2: {wantLang: 0x58, haveLang: 0x3e2, wantScript: 0x5a, haveScript: 0x20, distance: 0xa}, 2: {wantLang: 0x58, haveLang: 0x3e2, wantScript: 0x5b, haveScript: 0x20, distance: 0xa},
3: {wantLang: 0xa5, haveLang: 0x139, wantScript: 0xe, haveScript: 0x5a, distance: 0xa}, 3: {wantLang: 0xa5, haveLang: 0x139, wantScript: 0xe, haveScript: 0x5b, distance: 0xa},
4: {wantLang: 0x1d7, haveLang: 0x3e2, wantScript: 0x8, haveScript: 0x20, distance: 0xa}, 4: {wantLang: 0x1d7, haveLang: 0x3e2, wantScript: 0x8, haveScript: 0x20, distance: 0xa},
5: {wantLang: 0x210, haveLang: 0x139, wantScript: 0x2e, haveScript: 0x5a, distance: 0xa}, 5: {wantLang: 0x210, haveLang: 0x139, wantScript: 0x2e, haveScript: 0x5b, distance: 0xa},
6: {wantLang: 0x24a, haveLang: 0x139, wantScript: 0x4e, haveScript: 0x5a, distance: 0xa}, 6: {wantLang: 0x24a, haveLang: 0x139, wantScript: 0x4f, haveScript: 0x5b, distance: 0xa},
7: {wantLang: 0x251, haveLang: 0x139, wantScript: 0x52, haveScript: 0x5a, distance: 0xa}, 7: {wantLang: 0x251, haveLang: 0x139, wantScript: 0x53, haveScript: 0x5b, distance: 0xa},
8: {wantLang: 0x2b8, haveLang: 0x139, wantScript: 0x57, haveScript: 0x5a, distance: 0xa}, 8: {wantLang: 0x2b8, haveLang: 0x139, wantScript: 0x58, haveScript: 0x5b, distance: 0xa},
9: {wantLang: 0x304, haveLang: 0x139, wantScript: 0x6e, haveScript: 0x5a, distance: 0xa}, 9: {wantLang: 0x304, haveLang: 0x139, wantScript: 0x6f, haveScript: 0x5b, distance: 0xa},
10: {wantLang: 0x331, haveLang: 0x139, wantScript: 0x75, haveScript: 0x5a, distance: 0xa}, 10: {wantLang: 0x331, haveLang: 0x139, wantScript: 0x76, haveScript: 0x5b, distance: 0xa},
11: {wantLang: 0x351, haveLang: 0x139, wantScript: 0x22, haveScript: 0x5a, distance: 0xa}, 11: {wantLang: 0x351, haveLang: 0x139, wantScript: 0x22, haveScript: 0x5b, distance: 0xa},
12: {wantLang: 0x395, haveLang: 0x139, wantScript: 0x81, haveScript: 0x5a, distance: 0xa}, 12: {wantLang: 0x395, haveLang: 0x139, wantScript: 0x83, haveScript: 0x5b, distance: 0xa},
13: {wantLang: 0x39d, haveLang: 0x139, wantScript: 0x36, haveScript: 0x5a, distance: 0xa}, 13: {wantLang: 0x39d, haveLang: 0x139, wantScript: 0x36, haveScript: 0x5b, distance: 0xa},
14: {wantLang: 0x3be, haveLang: 0x139, wantScript: 0x5, haveScript: 0x5a, distance: 0xa}, 14: {wantLang: 0x3be, haveLang: 0x139, wantScript: 0x5, haveScript: 0x5b, distance: 0xa},
15: {wantLang: 0x3fa, haveLang: 0x139, wantScript: 0x5, haveScript: 0x5a, distance: 0xa}, 15: {wantLang: 0x3fa, haveLang: 0x139, wantScript: 0x5, haveScript: 0x5b, distance: 0xa},
16: {wantLang: 0x40c, haveLang: 0x139, wantScript: 0xd4, haveScript: 0x5a, distance: 0xa}, 16: {wantLang: 0x40c, haveLang: 0x139, wantScript: 0xd6, haveScript: 0x5b, distance: 0xa},
17: {wantLang: 0x450, haveLang: 0x139, wantScript: 0xe3, haveScript: 0x5a, distance: 0xa}, 17: {wantLang: 0x450, haveLang: 0x139, wantScript: 0xe6, haveScript: 0x5b, distance: 0xa},
18: {wantLang: 0x461, haveLang: 0x139, wantScript: 0xe6, haveScript: 0x5a, distance: 0xa}, 18: {wantLang: 0x461, haveLang: 0x139, wantScript: 0xe9, haveScript: 0x5b, distance: 0xa},
19: {wantLang: 0x46f, haveLang: 0x139, wantScript: 0x2c, haveScript: 0x5a, distance: 0xa}, 19: {wantLang: 0x46f, haveLang: 0x139, wantScript: 0x2c, haveScript: 0x5b, distance: 0xa},
20: {wantLang: 0x476, haveLang: 0x3e2, wantScript: 0x5a, haveScript: 0x20, distance: 0xa}, 20: {wantLang: 0x476, haveLang: 0x3e2, wantScript: 0x5b, haveScript: 0x20, distance: 0xa},
21: {wantLang: 0x4b4, haveLang: 0x139, wantScript: 0x5, haveScript: 0x5a, distance: 0xa}, 21: {wantLang: 0x4b4, haveLang: 0x139, wantScript: 0x5, haveScript: 0x5b, distance: 0xa},
22: {wantLang: 0x4bc, haveLang: 0x3e2, wantScript: 0x5a, haveScript: 0x20, distance: 0xa}, 22: {wantLang: 0x4bc, haveLang: 0x3e2, wantScript: 0x5b, haveScript: 0x20, distance: 0xa},
23: {wantLang: 0x512, haveLang: 0x139, wantScript: 0x3e, haveScript: 0x5a, distance: 0xa}, 23: {wantLang: 0x512, haveLang: 0x139, wantScript: 0x3e, haveScript: 0x5b, distance: 0xa},
24: {wantLang: 0x529, haveLang: 0x529, wantScript: 0x3b, haveScript: 0x3c, distance: 0xf}, 24: {wantLang: 0x529, haveLang: 0x529, wantScript: 0x3b, haveScript: 0x3c, distance: 0xf},
25: {wantLang: 0x529, haveLang: 0x529, wantScript: 0x3c, haveScript: 0x3b, distance: 0x13}, 25: {wantLang: 0x529, haveLang: 0x529, wantScript: 0x3c, haveScript: 0x3b, distance: 0x13},
} // Size: 232 bytes } // Size: 232 bytes
@ -295,4 +295,4 @@ var matchRegion = []regionIntelligibility{ // 15 elements
14: {lang: 0x529, script: 0x3c, group: 0x80, distance: 0x5}, 14: {lang: 0x529, script: 0x3c, group: 0x80, distance: 0x5},
} // Size: 114 bytes } // Size: 114 bytes
// Total table size 1472 bytes (1KiB); checksum: F86C669 // Total table size 1473 bytes (1KiB); checksum: 7BB90B5C

12
vendor/modules.txt vendored
View File

@ -1,7 +1,7 @@
# github.com/antchfx/xmlquery v1.3.15 # github.com/antchfx/xmlquery v1.4.1
## explicit; go 1.14 ## explicit; go 1.14
github.com/antchfx/xmlquery github.com/antchfx/xmlquery
# github.com/antchfx/xpath v1.2.4 # github.com/antchfx/xpath v1.3.1
## explicit; go 1.14 ## explicit; go 1.14
github.com/antchfx/xpath github.com/antchfx/xpath
# github.com/davecgh/go-spew v1.1.1 # github.com/davecgh/go-spew v1.1.1
@ -16,13 +16,13 @@ github.com/influxdata/influxdb1-client/pkg/escape
github.com/influxdata/influxdb1-client/v2 github.com/influxdata/influxdb1-client/v2
# github.com/stretchr/testify v1.7.0 # github.com/stretchr/testify v1.7.0
## explicit; go 1.13 ## explicit; go 1.13
# golang.org/x/net v0.8.0 # golang.org/x/net v0.28.0
## explicit; go 1.17 ## explicit; go 1.18
golang.org/x/net/html golang.org/x/net/html
golang.org/x/net/html/atom golang.org/x/net/html/atom
golang.org/x/net/html/charset golang.org/x/net/html/charset
# golang.org/x/text v0.8.0 # golang.org/x/text v0.17.0
## explicit; go 1.17 ## explicit; go 1.18
golang.org/x/text/encoding golang.org/x/text/encoding
golang.org/x/text/encoding/charmap golang.org/x/text/encoding/charmap
golang.org/x/text/encoding/htmlindex golang.org/x/text/encoding/htmlindex