Merge pull request #15905 from hashicorp/b-remove-xmlpath
deps: Remove xmlpath from dependencies
This commit is contained in:
commit
46bc77ff9a
|
@ -1,185 +0,0 @@
|
|||
This software is licensed under the LGPLv3, included below.
|
||||
|
||||
As a special exception to the GNU Lesser General Public License version 3
|
||||
("LGPL3"), the copyright holders of this Library give you permission to
|
||||
convey to a third party a Combined Work that links statically or dynamically
|
||||
to this Library without providing any Minimal Corresponding Source or
|
||||
Minimal Application Code as set out in 4d or providing the installation
|
||||
information set out in section 4e, provided that you comply with the other
|
||||
provisions of LGPL3 and provided that you meet, for the Application the
|
||||
terms and conditions of the license(s) which apply to the Application.
|
||||
|
||||
Except as stated in this special exception, the provisions of LGPL3 will
|
||||
continue to comply in full to this Library. If you modify this Library, you
|
||||
may apply this exception to your version of this Library, but you are not
|
||||
obliged to do so. If you do not wish to do so, delete this exception
|
||||
statement from your version. This exception does not (and cannot) modify any
|
||||
license terms which apply to the Application, with which you must still
|
||||
comply.
|
||||
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
|
@ -1,95 +0,0 @@
|
|||
// Package xmlpath implements a strict subset of the XPath specification for the Go language.
|
||||
//
|
||||
// The XPath specification is available at:
|
||||
//
|
||||
// http://www.w3.org/TR/xpath
|
||||
//
|
||||
// Path expressions supported by this package are in the following format,
|
||||
// with all components being optional:
|
||||
//
|
||||
// /axis-name::node-test[predicate]/axis-name::node-test[predicate]
|
||||
//
|
||||
// At the moment, xmlpath is compatible with the XPath specification
|
||||
// to the following extent:
|
||||
//
|
||||
// - All axes are supported ("child", "following-sibling", etc)
|
||||
// - All abbreviated forms are supported (".", "//", etc)
|
||||
// - All node types except for namespace are supported
|
||||
// - Predicates are restricted to [N], [path], and [path=literal] forms
|
||||
// - Only a single predicate is supported per path step
|
||||
// - Namespaces are experimentally supported
|
||||
// - Richer expressions
|
||||
//
|
||||
// For example, assuming the following document:
|
||||
//
|
||||
// <library>
|
||||
// <!-- Great book. -->
|
||||
// <book id="b0836217462" available="true">
|
||||
// <isbn>0836217462</isbn>
|
||||
// <title lang="en">Being a Dog Is a Full-Time Job</title>
|
||||
// <quote>I'd dog paddle the deepest ocean.</quote>
|
||||
// <author id="CMS">
|
||||
// <?echo "go rocks"?>
|
||||
// <name>Charles M Schulz</name>
|
||||
// <born>1922-11-26</born>
|
||||
// <dead>2000-02-12</dead>
|
||||
// </author>
|
||||
// <character id="PP">
|
||||
// <name>Peppermint Patty</name>
|
||||
// <born>1966-08-22</born>
|
||||
// <qualification>bold, brash and tomboyish</qualification>
|
||||
// </character>
|
||||
// <character id="Snoopy">
|
||||
// <name>Snoopy</name>
|
||||
// <born>1950-10-04</born>
|
||||
// <qualification>extroverted beagle</qualification>
|
||||
// </character>
|
||||
// </book>
|
||||
// </library>
|
||||
//
|
||||
// The following examples are valid path expressions, and the first
|
||||
// match has the indicated value:
|
||||
//
|
||||
// /library/book/isbn => "0836217462"
|
||||
// library/*/isbn => "0836217462"
|
||||
// /library/book/../book/./isbn => "0836217462"
|
||||
// /library/book/character[2]/name => "Snoopy"
|
||||
// /library/book/character[born='1950-10-04']/name => "Snoopy"
|
||||
// /library/book//node()[@id='PP']/name => "Peppermint Patty"
|
||||
// //book[author/@id='CMS']/title => "Being a Dog Is a Full-Time Job"},
|
||||
// /library/book/preceding::comment() => " Great book. "
|
||||
//
|
||||
// To run an expression, compile it, and then apply the compiled path to any
|
||||
// number of context nodes, from one or more parsed xml documents:
|
||||
//
|
||||
// path := xmlpath.MustCompile("/library/book/isbn")
|
||||
// root, err := xmlpath.Parse(file)
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
// if value, ok := path.String(root); ok {
|
||||
// fmt.Println("Found:", value)
|
||||
// }
|
||||
//
|
||||
// To use xmlpath with namespaces, it is required to give the supported set of namespace
|
||||
// when compiling:
|
||||
//
|
||||
//
|
||||
// var namespaces = []xmlpath.Namespace {
|
||||
// { "s", "http://www.w3.org/2003/05/soap-envelope" },
|
||||
// { "a", "http://schemas.xmlsoap.org/ws/2004/08/addressing" },
|
||||
// }
|
||||
// path, err := xmlpath.CompileWithNamespace("/s:Header/a:To", namespaces)
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
// root, err := xmlpath.Parse(file)
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
// if value, ok := path.String(root); ok {
|
||||
// fmt.Println("Found:", value)
|
||||
// }
|
||||
//
|
||||
|
||||
package xmlpath
|
|
@ -1,233 +0,0 @@
|
|||
package xmlpath
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Node is an item in an xml tree that was compiled to
|
||||
// be processed via xml paths. A node may represent:
|
||||
//
|
||||
// - An element in the xml document (<body>)
|
||||
// - An attribute of an element in the xml document (href="...")
|
||||
// - A comment in the xml document (<!--...-->)
|
||||
// - A processing instruction in the xml document (<?...?>)
|
||||
// - Some text within the xml document
|
||||
//
|
||||
type Node struct {
|
||||
kind nodeKind
|
||||
name xml.Name
|
||||
attr string
|
||||
text []byte
|
||||
|
||||
nodes []Node
|
||||
pos int
|
||||
end int
|
||||
|
||||
up *Node
|
||||
down []*Node
|
||||
}
|
||||
|
||||
type nodeKind int
|
||||
|
||||
const (
|
||||
anyNode nodeKind = iota
|
||||
startNode
|
||||
endNode
|
||||
attrNode
|
||||
textNode
|
||||
commentNode
|
||||
procInstNode
|
||||
)
|
||||
|
||||
// String returns the string value of node.
|
||||
//
|
||||
// The string value of a node is:
|
||||
//
|
||||
// - For element nodes, the concatenation of all text nodes within the element.
|
||||
// - For text nodes, the text itself.
|
||||
// - For attribute nodes, the attribute value.
|
||||
// - For comment nodes, the text within the comment delimiters.
|
||||
// - For processing instruction nodes, the content of the instruction.
|
||||
//
|
||||
func (node *Node) String() string {
|
||||
if node.kind == attrNode {
|
||||
return node.attr
|
||||
}
|
||||
return string(node.Bytes())
|
||||
}
|
||||
|
||||
// Bytes returns the string value of node as a byte slice.
|
||||
// See Node.String for a description of what the string value of a node is.
|
||||
func (node *Node) Bytes() []byte {
|
||||
if node.kind == attrNode {
|
||||
return []byte(node.attr)
|
||||
}
|
||||
if node.kind != startNode {
|
||||
return node.text
|
||||
}
|
||||
var text []byte
|
||||
for i := node.pos; i < node.end; i++ {
|
||||
if node.nodes[i].kind == textNode {
|
||||
text = append(text, node.nodes[i].text...)
|
||||
}
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
// equals returns whether the string value of node is equal to s,
|
||||
// without allocating memory.
|
||||
func (node *Node) equals(s string) bool {
|
||||
if node.kind == attrNode {
|
||||
return s == node.attr
|
||||
}
|
||||
if node.kind != startNode {
|
||||
if len(s) != len(node.text) {
|
||||
return false
|
||||
}
|
||||
for i := range s {
|
||||
if s[i] != node.text[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
si := 0
|
||||
for i := node.pos; i < node.end; i++ {
|
||||
if node.nodes[i].kind == textNode {
|
||||
for _, c := range node.nodes[i].text {
|
||||
if si > len(s) {
|
||||
return false
|
||||
}
|
||||
if s[si] != c {
|
||||
return false
|
||||
}
|
||||
si++
|
||||
}
|
||||
}
|
||||
}
|
||||
return si == len(s)
|
||||
}
|
||||
|
||||
// Parse reads an xml document from r, parses it, and returns its root node.
|
||||
func Parse(r io.Reader) (*Node, error) {
|
||||
return ParseDecoder(xml.NewDecoder(r))
|
||||
}
|
||||
|
||||
// ParseHTML reads an HTML-like document from r, parses it, and returns
|
||||
// its root node.
|
||||
func ParseHTML(r io.Reader) (*Node, error) {
|
||||
d := xml.NewDecoder(r)
|
||||
d.Strict = false
|
||||
d.AutoClose = xml.HTMLAutoClose
|
||||
d.Entity = xml.HTMLEntity
|
||||
return ParseDecoder(d)
|
||||
}
|
||||
|
||||
// ParseDecoder parses the xml document being decoded by d and returns
|
||||
// its root node.
|
||||
func ParseDecoder(d *xml.Decoder) (*Node, error) {
|
||||
var nodes []Node
|
||||
var text []byte
|
||||
|
||||
// The root node.
|
||||
nodes = append(nodes, Node{kind: startNode})
|
||||
|
||||
for {
|
||||
t, err := d.Token()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch t := t.(type) {
|
||||
case xml.EndElement:
|
||||
nodes = append(nodes, Node{
|
||||
kind: endNode,
|
||||
})
|
||||
case xml.StartElement:
|
||||
nodes = append(nodes, Node{
|
||||
kind: startNode,
|
||||
name: t.Name,
|
||||
})
|
||||
for _, attr := range t.Attr {
|
||||
nodes = append(nodes, Node{
|
||||
kind: attrNode,
|
||||
name: attr.Name,
|
||||
attr: attr.Value,
|
||||
})
|
||||
}
|
||||
case xml.CharData:
|
||||
texti := len(text)
|
||||
text = append(text, t...)
|
||||
nodes = append(nodes, Node{
|
||||
kind: textNode,
|
||||
text: text[texti : texti+len(t)],
|
||||
})
|
||||
case xml.Comment:
|
||||
texti := len(text)
|
||||
text = append(text, t...)
|
||||
nodes = append(nodes, Node{
|
||||
kind: commentNode,
|
||||
text: text[texti : texti+len(t)],
|
||||
})
|
||||
case xml.ProcInst:
|
||||
texti := len(text)
|
||||
text = append(text, t.Inst...)
|
||||
nodes = append(nodes, Node{
|
||||
kind: procInstNode,
|
||||
name: xml.Name{Local: t.Target},
|
||||
text: text[texti : texti+len(t.Inst)],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Close the root node.
|
||||
nodes = append(nodes, Node{kind: endNode})
|
||||
|
||||
stack := make([]*Node, 0, len(nodes))
|
||||
downs := make([]*Node, len(nodes))
|
||||
downCount := 0
|
||||
|
||||
for pos := range nodes {
|
||||
|
||||
switch nodes[pos].kind {
|
||||
|
||||
case startNode, attrNode, textNode, commentNode, procInstNode:
|
||||
node := &nodes[pos]
|
||||
node.nodes = nodes
|
||||
node.pos = pos
|
||||
if len(stack) > 0 {
|
||||
node.up = stack[len(stack)-1]
|
||||
}
|
||||
if node.kind == startNode {
|
||||
stack = append(stack, node)
|
||||
} else {
|
||||
node.end = pos + 1
|
||||
}
|
||||
|
||||
case endNode:
|
||||
node := stack[len(stack)-1]
|
||||
node.end = pos
|
||||
stack = stack[:len(stack)-1]
|
||||
|
||||
// Compute downs. Doing that here is what enables the
|
||||
// use of a slice of a contiguous pre-allocated block.
|
||||
node.down = downs[downCount:downCount]
|
||||
for i := node.pos + 1; i < node.end; i++ {
|
||||
if nodes[i].up == node {
|
||||
switch nodes[i].kind {
|
||||
case startNode, textNode, commentNode, procInstNode:
|
||||
node.down = append(node.down, &nodes[i])
|
||||
downCount++
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(stack) == 0 {
|
||||
return node, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, io.EOF
|
||||
}
|
|
@ -1,642 +0,0 @@
|
|||
package xmlpath
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// Namespace represents a given XML Namespace
|
||||
type Namespace struct {
|
||||
Prefix string
|
||||
Uri string
|
||||
}
|
||||
|
||||
// Path is a compiled path that can be applied to a context
|
||||
// node to obtain a matching node set.
|
||||
// A single Path can be applied concurrently to any number
|
||||
// of context nodes.
|
||||
type Path struct {
|
||||
path string
|
||||
steps []pathStep
|
||||
}
|
||||
|
||||
// Iter returns an iterator that goes over the list of nodes
|
||||
// that p matches on the given context.
|
||||
func (p *Path) Iter(context *Node) *Iter {
|
||||
iter := Iter{
|
||||
make([]pathStepState, len(p.steps)),
|
||||
make([]bool, len(context.nodes)),
|
||||
}
|
||||
for i := range p.steps {
|
||||
iter.state[i].step = &p.steps[i]
|
||||
}
|
||||
iter.state[0].init(context)
|
||||
return &iter
|
||||
}
|
||||
|
||||
// Exists returns whether any nodes match p on the given context.
|
||||
func (p *Path) Exists(context *Node) bool {
|
||||
return p.Iter(context).Next()
|
||||
}
|
||||
|
||||
// String returns the string value of the first node matched
|
||||
// by p on the given context.
|
||||
//
|
||||
// See the documentation of Node.String.
|
||||
func (p *Path) String(context *Node) (s string, ok bool) {
|
||||
iter := p.Iter(context)
|
||||
if iter.Next() {
|
||||
return iter.Node().String(), true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
// Bytes returns as a byte slice the string value of the first
|
||||
// node matched by p on the given context.
|
||||
//
|
||||
// See the documentation of Node.String.
|
||||
func (p *Path) Bytes(node *Node) (b []byte, ok bool) {
|
||||
iter := p.Iter(node)
|
||||
if iter.Next() {
|
||||
return iter.Node().Bytes(), true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Iter iterates over node sets.
|
||||
type Iter struct {
|
||||
state []pathStepState
|
||||
seen []bool
|
||||
}
|
||||
|
||||
// Node returns the current node.
|
||||
// Must only be called after Iter.Next returns true.
|
||||
func (iter *Iter) Node() *Node {
|
||||
state := iter.state[len(iter.state)-1]
|
||||
if state.pos == 0 {
|
||||
panic("Iter.Node called before Iter.Next")
|
||||
}
|
||||
if state.node == nil {
|
||||
panic("Iter.Node called after Iter.Next false")
|
||||
}
|
||||
return state.node
|
||||
}
|
||||
|
||||
// Next iterates to the next node in the set, if any, and
|
||||
// returns whether there is a node available.
|
||||
func (iter *Iter) Next() bool {
|
||||
tip := len(iter.state) - 1
|
||||
outer:
|
||||
for {
|
||||
for !iter.state[tip].next() {
|
||||
tip--
|
||||
if tip == -1 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for tip < len(iter.state)-1 {
|
||||
tip++
|
||||
iter.state[tip].init(iter.state[tip-1].node)
|
||||
if !iter.state[tip].next() {
|
||||
tip--
|
||||
continue outer
|
||||
}
|
||||
}
|
||||
if iter.seen[iter.state[tip].node.pos] {
|
||||
continue
|
||||
}
|
||||
iter.seen[iter.state[tip].node.pos] = true
|
||||
return true
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
type pathStepState struct {
|
||||
step *pathStep
|
||||
node *Node
|
||||
pos int
|
||||
idx int
|
||||
aux int
|
||||
}
|
||||
|
||||
func (s *pathStepState) init(node *Node) {
|
||||
s.node = node
|
||||
s.pos = 0
|
||||
s.idx = 0
|
||||
s.aux = 0
|
||||
}
|
||||
|
||||
func (s *pathStepState) next() bool {
|
||||
for s._next() {
|
||||
s.pos++
|
||||
if s.step.pred == nil {
|
||||
return true
|
||||
}
|
||||
if s.step.pred.bval {
|
||||
if s.step.pred.path.Exists(s.node) {
|
||||
return true
|
||||
}
|
||||
} else if s.step.pred.path != nil {
|
||||
iter := s.step.pred.path.Iter(s.node)
|
||||
for iter.Next() {
|
||||
if iter.Node().equals(s.step.pred.sval) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if s.step.pred.ival == s.pos {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *pathStepState) _next() bool {
|
||||
if s.node == nil {
|
||||
return false
|
||||
}
|
||||
if s.step.root && s.idx == 0 {
|
||||
for s.node.up != nil {
|
||||
s.node = s.node.up
|
||||
}
|
||||
}
|
||||
|
||||
switch s.step.axis {
|
||||
|
||||
case "self":
|
||||
if s.idx == 0 && s.step.match(s.node) {
|
||||
s.idx++
|
||||
return true
|
||||
}
|
||||
|
||||
case "parent":
|
||||
if s.idx == 0 && s.node.up != nil && s.step.match(s.node.up) {
|
||||
s.idx++
|
||||
s.node = s.node.up
|
||||
return true
|
||||
}
|
||||
|
||||
case "ancestor", "ancestor-or-self":
|
||||
if s.idx == 0 && s.step.axis == "ancestor-or-self" {
|
||||
s.idx++
|
||||
if s.step.match(s.node) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for s.node.up != nil {
|
||||
s.node = s.node.up
|
||||
s.idx++
|
||||
if s.step.match(s.node) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
case "child":
|
||||
var down []*Node
|
||||
if s.idx == 0 {
|
||||
down = s.node.down
|
||||
} else {
|
||||
down = s.node.up.down
|
||||
}
|
||||
for s.idx < len(down) {
|
||||
node := down[s.idx]
|
||||
s.idx++
|
||||
if s.step.match(node) {
|
||||
s.node = node
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
case "descendant", "descendant-or-self":
|
||||
if s.idx == 0 {
|
||||
s.idx = s.node.pos
|
||||
s.aux = s.node.end
|
||||
if s.step.axis == "descendant" {
|
||||
s.idx++
|
||||
}
|
||||
}
|
||||
for s.idx < s.aux {
|
||||
node := &s.node.nodes[s.idx]
|
||||
s.idx++
|
||||
if node.kind == attrNode {
|
||||
continue
|
||||
}
|
||||
if s.step.match(node) {
|
||||
s.node = node
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
case "following":
|
||||
if s.idx == 0 {
|
||||
s.idx = s.node.end
|
||||
}
|
||||
for s.idx < len(s.node.nodes) {
|
||||
node := &s.node.nodes[s.idx]
|
||||
s.idx++
|
||||
if node.kind == attrNode {
|
||||
continue
|
||||
}
|
||||
if s.step.match(node) {
|
||||
s.node = node
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
case "following-sibling":
|
||||
var down []*Node
|
||||
if s.node.up != nil {
|
||||
down = s.node.up.down
|
||||
if s.idx == 0 {
|
||||
for s.idx < len(down) {
|
||||
node := down[s.idx]
|
||||
s.idx++
|
||||
if node == s.node {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for s.idx < len(down) {
|
||||
node := down[s.idx]
|
||||
s.idx++
|
||||
if s.step.match(node) {
|
||||
s.node = node
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
case "preceding":
|
||||
if s.idx == 0 {
|
||||
s.aux = s.node.pos // Detect ancestors.
|
||||
s.idx = s.node.pos - 1
|
||||
}
|
||||
for s.idx >= 0 {
|
||||
node := &s.node.nodes[s.idx]
|
||||
s.idx--
|
||||
if node.kind == attrNode {
|
||||
continue
|
||||
}
|
||||
if node == s.node.nodes[s.aux].up {
|
||||
s.aux = s.node.nodes[s.aux].up.pos
|
||||
continue
|
||||
}
|
||||
if s.step.match(node) {
|
||||
s.node = node
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
case "preceding-sibling":
|
||||
var down []*Node
|
||||
if s.node.up != nil {
|
||||
down = s.node.up.down
|
||||
if s.aux == 0 {
|
||||
s.aux = 1
|
||||
for s.idx < len(down) {
|
||||
node := down[s.idx]
|
||||
s.idx++
|
||||
if node == s.node {
|
||||
s.idx--
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for s.idx >= 0 {
|
||||
node := down[s.idx]
|
||||
s.idx--
|
||||
if s.step.match(node) {
|
||||
s.node = node
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
case "attribute":
|
||||
if s.idx == 0 {
|
||||
s.idx = s.node.pos + 1
|
||||
s.aux = s.node.end
|
||||
}
|
||||
for s.idx < s.aux {
|
||||
node := &s.node.nodes[s.idx]
|
||||
s.idx++
|
||||
if node.kind != attrNode {
|
||||
break
|
||||
}
|
||||
if s.step.match(node) {
|
||||
s.node = node
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
s.node = nil
|
||||
return false
|
||||
}
|
||||
|
||||
type pathPredicate struct {
|
||||
path *Path
|
||||
sval string
|
||||
ival int
|
||||
bval bool
|
||||
}
|
||||
|
||||
type pathStep struct {
|
||||
root bool
|
||||
axis string
|
||||
name string
|
||||
prefix string
|
||||
uri string
|
||||
kind nodeKind
|
||||
pred *pathPredicate
|
||||
}
|
||||
|
||||
func (step *pathStep) match(node *Node) bool {
|
||||
return node.kind != endNode &&
|
||||
(step.kind == anyNode || step.kind == node.kind) &&
|
||||
(step.name == "*" || (node.name.Local == step.name && (node.name.Space != "" && node.name.Space == step.uri || node.name.Space == "")))
|
||||
}
|
||||
|
||||
// MustCompile returns the compiled path, and panics if
|
||||
// there are any errors.
|
||||
func MustCompile(path string) *Path {
|
||||
e, err := Compile(path)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
// Compile returns the compiled path.
|
||||
func Compile(path string) (*Path, error) {
|
||||
c := pathCompiler{path, 0, []Namespace{} }
|
||||
if path == "" {
|
||||
return nil, c.errorf("empty path")
|
||||
}
|
||||
p, err := c.parsePath()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// Compile the path with the knowledge of the given namespaces
|
||||
func CompileWithNamespace(path string, ns []Namespace) (*Path, error) {
|
||||
c := pathCompiler{path, 0, ns}
|
||||
if path == "" {
|
||||
return nil, c.errorf("empty path")
|
||||
}
|
||||
p, err := c.parsePath()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
type pathCompiler struct {
|
||||
path string
|
||||
i int
|
||||
ns []Namespace
|
||||
}
|
||||
|
||||
func (c *pathCompiler) errorf(format string, args ...interface{}) error {
|
||||
return fmt.Errorf("compiling xml path %q:%d: %s", c.path, c.i, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
func (c *pathCompiler) parsePath() (path *Path, err error) {
|
||||
var steps []pathStep
|
||||
var start = c.i
|
||||
for {
|
||||
step := pathStep{axis: "child"}
|
||||
|
||||
if c.i == 0 && c.skipByte('/') {
|
||||
step.root = true
|
||||
if len(c.path) == 1 {
|
||||
step.name = "*"
|
||||
}
|
||||
}
|
||||
if c.peekByte('/') {
|
||||
step.axis = "descendant-or-self"
|
||||
step.name = "*"
|
||||
} else if c.skipByte('@') {
|
||||
mark := c.i
|
||||
if !c.skipName() {
|
||||
return nil, c.errorf("missing name after @")
|
||||
}
|
||||
step.axis = "attribute"
|
||||
step.name = c.path[mark:c.i]
|
||||
step.kind = attrNode
|
||||
} else {
|
||||
mark := c.i
|
||||
if c.skipName() {
|
||||
step.name = c.path[mark:c.i]
|
||||
}
|
||||
if step.name == "" {
|
||||
return nil, c.errorf("missing name")
|
||||
} else if step.name == "*" {
|
||||
step.kind = startNode
|
||||
} else if step.name == "." {
|
||||
step.axis = "self"
|
||||
step.name = "*"
|
||||
} else if step.name == ".." {
|
||||
step.axis = "parent"
|
||||
step.name = "*"
|
||||
} else {
|
||||
if c.skipByte(':') {
|
||||
if !c.skipByte(':') {
|
||||
mark = c.i
|
||||
if c.skipName() {
|
||||
step.prefix = step.name
|
||||
step.name = c.path[mark:c.i]
|
||||
// check prefix
|
||||
found := false
|
||||
for _, ns := range c.ns {
|
||||
if ns.Prefix == step.prefix {
|
||||
step.uri = ns.Uri
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return nil, c.errorf("unknown namespace prefix: %s", step.prefix)
|
||||
}
|
||||
} else {
|
||||
return nil, c.errorf("missing name after namespace prefix")
|
||||
}
|
||||
} else {
|
||||
switch step.name {
|
||||
case "attribute":
|
||||
step.kind = attrNode
|
||||
case "self", "child", "parent":
|
||||
case "descendant", "descendant-or-self":
|
||||
case "ancestor", "ancestor-or-self":
|
||||
case "following", "following-sibling":
|
||||
case "preceding", "preceding-sibling":
|
||||
default:
|
||||
return nil, c.errorf("unsupported axis: %q", step.name)
|
||||
}
|
||||
step.axis = step.name
|
||||
|
||||
mark = c.i
|
||||
if !c.skipName() {
|
||||
return nil, c.errorf("missing name")
|
||||
}
|
||||
step.name = c.path[mark:c.i]
|
||||
}
|
||||
}
|
||||
if c.skipByte('(') {
|
||||
conflict := step.kind != anyNode
|
||||
switch step.name {
|
||||
case "node":
|
||||
// must be anyNode
|
||||
case "text":
|
||||
step.kind = textNode
|
||||
case "comment":
|
||||
step.kind = commentNode
|
||||
case "processing-instruction":
|
||||
step.kind = procInstNode
|
||||
default:
|
||||
return nil, c.errorf("unsupported expression: %s()", step.name)
|
||||
}
|
||||
if conflict {
|
||||
return nil, c.errorf("%s() cannot succeed on axis %q", step.name, step.axis)
|
||||
}
|
||||
|
||||
literal, err := c.parseLiteral()
|
||||
if err == errNoLiteral {
|
||||
step.name = "*"
|
||||
} else if err != nil {
|
||||
return nil, c.errorf("%v", err)
|
||||
} else if step.kind == procInstNode {
|
||||
step.name = literal
|
||||
} else {
|
||||
return nil, c.errorf("%s() has no arguments", step.name)
|
||||
}
|
||||
if !c.skipByte(')') {
|
||||
return nil, c.errorf("missing )")
|
||||
}
|
||||
} else if step.name == "*" && step.kind == anyNode {
|
||||
step.kind = startNode
|
||||
}
|
||||
}
|
||||
}
|
||||
if c.skipByte('[') {
|
||||
step.pred = &pathPredicate{}
|
||||
if ival, ok := c.parseInt(); ok {
|
||||
if ival == 0 {
|
||||
return nil, c.errorf("positions start at 1")
|
||||
}
|
||||
step.pred.ival = ival
|
||||
} else {
|
||||
path, err := c.parsePath()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if path.path[0] == '-' {
|
||||
if _, err = strconv.Atoi(path.path); err == nil {
|
||||
return nil, c.errorf("positions must be positive")
|
||||
}
|
||||
}
|
||||
step.pred.path = path
|
||||
if c.skipByte('=') {
|
||||
sval, err := c.parseLiteral()
|
||||
if err != nil {
|
||||
return nil, c.errorf("%v", err)
|
||||
}
|
||||
step.pred.sval = sval
|
||||
} else {
|
||||
step.pred.bval = true
|
||||
}
|
||||
}
|
||||
if !c.skipByte(']') {
|
||||
return nil, c.errorf("expected ']'")
|
||||
}
|
||||
}
|
||||
steps = append(steps, step)
|
||||
//fmt.Printf("step: %#v\n", step)
|
||||
if !c.skipByte('/') {
|
||||
if (start == 0 || start == c.i) && c.i < len(c.path) {
|
||||
return nil, c.errorf("unexpected %q", c.path[c.i])
|
||||
}
|
||||
return &Path{steps: steps, path: c.path[start:c.i]}, nil
|
||||
}
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
var errNoLiteral = fmt.Errorf("expected a literal string")
|
||||
|
||||
func (c *pathCompiler) parseLiteral() (string, error) {
|
||||
if c.skipByte('"') {
|
||||
mark := c.i
|
||||
if !c.skipByteFind('"') {
|
||||
return "", fmt.Errorf(`missing '"'`)
|
||||
}
|
||||
return c.path[mark:c.i-1], nil
|
||||
}
|
||||
if c.skipByte('\'') {
|
||||
mark := c.i
|
||||
if !c.skipByteFind('\'') {
|
||||
return "", fmt.Errorf(`missing "'"`)
|
||||
}
|
||||
return c.path[mark:c.i-1], nil
|
||||
}
|
||||
return "", errNoLiteral
|
||||
}
|
||||
|
||||
func (c *pathCompiler) parseInt() (v int, ok bool) {
|
||||
mark := c.i
|
||||
for c.i < len(c.path) && c.path[c.i] >= '0' && c.path[c.i] <= '9' {
|
||||
v *= 10
|
||||
v += int(c.path[c.i]) - '0'
|
||||
c.i++
|
||||
}
|
||||
if c.i == mark {
|
||||
return 0, false
|
||||
}
|
||||
return v, true
|
||||
}
|
||||
|
||||
func (c *pathCompiler) skipByte(b byte) bool {
|
||||
if c.i < len(c.path) && c.path[c.i] == b {
|
||||
c.i++
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *pathCompiler) skipByteFind(b byte) bool {
|
||||
for i := c.i; i < len(c.path); i++ {
|
||||
if c.path[i] == b {
|
||||
c.i = i+1
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *pathCompiler) peekByte(b byte) bool {
|
||||
return c.i < len(c.path) && c.path[c.i] == b
|
||||
}
|
||||
|
||||
func (c *pathCompiler) skipName() bool {
|
||||
if c.i >= len(c.path) {
|
||||
return false
|
||||
}
|
||||
if c.path[c.i] == '*' {
|
||||
c.i++
|
||||
return true
|
||||
}
|
||||
start := c.i
|
||||
for c.i < len(c.path) && (c.path[c.i] >= utf8.RuneSelf || isNameByte(c.path[c.i])) {
|
||||
c.i++
|
||||
}
|
||||
return c.i > start
|
||||
}
|
||||
|
||||
func isNameByte(c byte) bool {
|
||||
return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '_' || c == '.' || c == '-'
|
||||
}
|
|
@ -1688,11 +1688,6 @@
|
|||
"revision": "a2df6b1315e6fd5885eb15c67ed259e85854125f",
|
||||
"revisionTime": "2017-08-14T13:39:27Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "bx+egnFe0OB0BZBcgZcaqsvcmS4=",
|
||||
"path": "github.com/masterzen/xmlpath",
|
||||
"revision": "13f4951698adc0fa9c1dda3e275d489a24201161"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "rCffFCN6TpDAN3Jylyo8RFzhQ9E=",
|
||||
"path": "github.com/mattn/go-colorable",
|
||||
|
|
Loading…
Reference in New Issue