初始化项目
This commit is contained in:
301
trunk/goutil/xmlUtil/gxpath/internal/build/build.go
Normal file
301
trunk/goutil/xmlUtil/gxpath/internal/build/build.go
Normal file
@@ -0,0 +1,301 @@
|
||||
package build
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"goutil/xmlUtil/gxpath/internal/parse"
|
||||
"goutil/xmlUtil/gxpath/internal/query"
|
||||
"goutil/xmlUtil/gxpath/xpath"
|
||||
)
|
||||
|
||||
type flag int
|
||||
|
||||
const (
|
||||
noneFlag flag = iota
|
||||
filterFlag
|
||||
)
|
||||
|
||||
// builder provides building an XPath expressions.
|
||||
type builder struct {
|
||||
depth int
|
||||
flag flag
|
||||
firstInput query.Query
|
||||
}
|
||||
|
||||
// axisPredicate creates a predicate to predicating for this axis node.
|
||||
func axisPredicate(root *parse.AxisNode) func(xpath.NodeNavigator) bool {
|
||||
// get current axix node type.
|
||||
typ := xpath.ElementNode
|
||||
if root.AxeType == "attribute" {
|
||||
typ = xpath.AttributeNode
|
||||
} else {
|
||||
switch root.Prop {
|
||||
case "comment":
|
||||
typ = xpath.CommentNode
|
||||
case "text":
|
||||
typ = xpath.TextNode
|
||||
// case "processing-instruction":
|
||||
// typ = xpath.ProcessingInstructionNode
|
||||
case "node":
|
||||
typ = xpath.ElementNode
|
||||
}
|
||||
}
|
||||
predicate := func(n xpath.NodeNavigator) bool {
|
||||
if typ == n.NodeType() {
|
||||
if root.LocalName == "" || (root.LocalName == n.LocalName() && root.Prefix == n.Prefix()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
return predicate
|
||||
}
|
||||
|
||||
// processAxisNode buildes a query for the XPath axis node.
|
||||
func (b *builder) processAxisNode(root *parse.AxisNode) (query.Query, error) {
|
||||
var (
|
||||
err error
|
||||
qyInput query.Query
|
||||
qyOutput query.Query
|
||||
predicate = axisPredicate(root)
|
||||
)
|
||||
|
||||
if root.Input == nil {
|
||||
qyInput = &query.ContextQuery{}
|
||||
} else {
|
||||
if b.flag&filterFlag == 0 {
|
||||
if root.AxeType == "child" && (root.Input.Type() == parse.NodeAxis) {
|
||||
if input := root.Input.(*parse.AxisNode); input.AxeType == "descendant-or-self" {
|
||||
var qyGrandInput query.Query
|
||||
if input.Input != nil {
|
||||
qyGrandInput, _ = b.processNode(input.Input)
|
||||
} else {
|
||||
qyGrandInput = &query.ContextQuery{}
|
||||
}
|
||||
qyOutput = &query.DescendantQuery{Input: qyGrandInput, Predicate: predicate, Self: true}
|
||||
return qyOutput, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
qyInput, err = b.processNode(root.Input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
switch root.AxeType {
|
||||
case "ancestor":
|
||||
qyOutput = &query.AncestorQuery{Input: qyInput, Predicate: predicate}
|
||||
case "ancestor-or-self":
|
||||
qyOutput = &query.AncestorQuery{Input: qyInput, Predicate: predicate, Self: true}
|
||||
case "attribute":
|
||||
qyOutput = &query.AttributeQuery{Input: qyInput, Predicate: predicate}
|
||||
case "child":
|
||||
filter := func(n xpath.NodeNavigator) bool {
|
||||
v := predicate(n)
|
||||
switch root.Prop {
|
||||
case "text":
|
||||
v = v && n.NodeType() == xpath.TextNode
|
||||
case "node":
|
||||
v = v && (n.NodeType() == xpath.ElementNode || n.NodeType() == xpath.TextNode)
|
||||
case "comment":
|
||||
v = v && n.NodeType() == xpath.CommentNode
|
||||
}
|
||||
return v
|
||||
}
|
||||
qyOutput = &query.ChildQuery{Input: qyInput, Predicate: filter}
|
||||
case "descendant":
|
||||
qyOutput = &query.DescendantQuery{Input: qyInput, Predicate: predicate}
|
||||
case "descendant-or-self":
|
||||
qyOutput = &query.DescendantQuery{Input: qyInput, Predicate: predicate, Self: true}
|
||||
case "following":
|
||||
qyOutput = &query.FollowingQuery{Input: qyInput, Predicate: predicate}
|
||||
case "following-sibling":
|
||||
qyOutput = &query.FollowingQuery{Input: qyInput, Predicate: predicate, Sibling: true}
|
||||
case "parent":
|
||||
qyOutput = &query.ParentQuery{Input: qyInput, Predicate: predicate}
|
||||
case "preceding":
|
||||
qyOutput = &query.PrecedingQuery{Input: qyInput, Predicate: predicate}
|
||||
case "preceding-sibling":
|
||||
qyOutput = &query.PrecedingQuery{Input: qyInput, Predicate: predicate, Sibling: true}
|
||||
case "self":
|
||||
qyOutput = &query.SelfQuery{Input: qyInput, Predicate: predicate}
|
||||
case "namespace":
|
||||
// haha,what will you do someting??
|
||||
default:
|
||||
err = fmt.Errorf("unknown axe type: %s", root.AxeType)
|
||||
return nil, err
|
||||
}
|
||||
return qyOutput, nil
|
||||
}
|
||||
|
||||
// processFilterNode builds query.Query for the XPath filter predicate.
|
||||
func (b *builder) processFilterNode(root *parse.FilterNode) (query.Query, error) {
|
||||
b.flag |= filterFlag
|
||||
|
||||
qyInput, err := b.processNode(root.Input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
qyCond, err := b.processNode(root.Condition)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
qyOutput := &query.FilterQuery{Input: qyInput, Predicate: qyCond}
|
||||
return qyOutput, nil
|
||||
}
|
||||
|
||||
// processFunctionNode buildes query.Query for the XPath function node.
|
||||
func (b *builder) processFunctionNode(root *parse.FunctionNode) (query.Query, error) {
|
||||
var qyOutput query.Query
|
||||
switch root.FuncName {
|
||||
case "starts-with":
|
||||
arg1, err := b.processNode(root.Args[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
arg2, err := b.processNode(root.Args[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
startwith := &startwithFunc{arg1, arg2}
|
||||
qyOutput = &query.XPathFunction{Input: b.firstInput, Func: startwith.do}
|
||||
case "substring":
|
||||
//substring( string , start [, length] )
|
||||
if len(root.Args) < 2 {
|
||||
return nil, errors.New("xpath: substring function must have at least two parameter")
|
||||
}
|
||||
var (
|
||||
arg1, arg2, arg3 query.Query
|
||||
err error
|
||||
)
|
||||
if arg1, err = b.processNode(root.Args[0]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if arg2, err = b.processNode(root.Args[1]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(root.Args) == 3 {
|
||||
if arg3, err = b.processNode(root.Args[2]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
substring := &substringFunc{arg1, arg2, arg3}
|
||||
qyOutput = &query.XPathFunction{Input: b.firstInput, Func: substring.do}
|
||||
case "normalize-space":
|
||||
if len(root.Args) == 0 {
|
||||
return nil, errors.New("xpath: normalize-space function must have at least one parameter")
|
||||
}
|
||||
argQuery, err := b.processNode(root.Args[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
qyOutput = &query.XPathFunction{Input: argQuery, Func: normalizespaceFunc}
|
||||
case "name":
|
||||
qyOutput = &query.XPathFunction{Input: b.firstInput, Func: nameFunc}
|
||||
case "last":
|
||||
qyOutput = &query.XPathFunction{Input: b.firstInput, Func: lastFunc}
|
||||
case "position":
|
||||
qyOutput = &query.XPathFunction{Input: b.firstInput, Func: positionFunc}
|
||||
case "count":
|
||||
if b.firstInput == nil {
|
||||
return nil, errors.New("xpath: expression must evaluate to node-set")
|
||||
}
|
||||
if len(root.Args) == 0 {
|
||||
return nil, fmt.Errorf("xpath: count(node-sets) function must with have parameters node-sets")
|
||||
}
|
||||
argQuery, err := b.processNode(root.Args[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
qyOutput = &query.XPathFunction{Input: argQuery, Func: countFunc}
|
||||
default:
|
||||
return nil, fmt.Errorf("not yet support this function %s()", root.FuncName)
|
||||
}
|
||||
return qyOutput, nil
|
||||
}
|
||||
|
||||
func (b *builder) processOperatorNode(root *parse.OperatorNode) (query.Query, error) {
|
||||
left, err := b.processNode(root.Left)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
right, err := b.processNode(root.Right)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var qyOutput query.Query
|
||||
switch root.Op {
|
||||
case "+", "-", "div", "mod": // Numeric operator
|
||||
var exprFunc func(interface{}, interface{}) interface{}
|
||||
switch root.Op {
|
||||
case "+":
|
||||
exprFunc = plusFunc
|
||||
case "-":
|
||||
exprFunc = minusFunc
|
||||
case "div":
|
||||
exprFunc = divFunc
|
||||
case "mod":
|
||||
exprFunc = modFunc
|
||||
}
|
||||
qyOutput = &query.NumericExpr{Left: left, Right: right, Do: exprFunc}
|
||||
case "=", ">", ">=", "<", "<=", "!=":
|
||||
var exprFunc func(query.Iterator, interface{}, interface{}) interface{}
|
||||
switch root.Op {
|
||||
case "=":
|
||||
exprFunc = eqFunc
|
||||
case ">":
|
||||
exprFunc = gtFunc
|
||||
case ">=":
|
||||
exprFunc = geFunc
|
||||
case "<":
|
||||
exprFunc = ltFunc
|
||||
case "<=":
|
||||
exprFunc = leFunc
|
||||
case "!=":
|
||||
exprFunc = neFunc
|
||||
}
|
||||
qyOutput = &query.LogicalExpr{Left: left, Right: right, Do: exprFunc}
|
||||
case "or", "and", "|":
|
||||
isOr := false
|
||||
if root.Op == "or" || root.Op == "|" {
|
||||
isOr = true
|
||||
}
|
||||
qyOutput = &query.BooleanExpr{Left: left, Right: right, IsOr: isOr}
|
||||
}
|
||||
return qyOutput, nil
|
||||
}
|
||||
|
||||
func (b *builder) processNode(root parse.Node) (q query.Query, err error) {
|
||||
if b.depth = b.depth + 1; b.depth > 1024 {
|
||||
err = errors.New("the xpath expressions is too complex")
|
||||
return
|
||||
}
|
||||
|
||||
switch root.Type() {
|
||||
case parse.NodeConstantOperand:
|
||||
n := root.(*parse.OperandNode)
|
||||
q = &query.XPathConstant{Val: n.Val}
|
||||
case parse.NodeRoot:
|
||||
q = &query.ContextQuery{Root: true}
|
||||
case parse.NodeAxis:
|
||||
q, err = b.processAxisNode(root.(*parse.AxisNode))
|
||||
b.firstInput = q
|
||||
case parse.NodeFilter:
|
||||
q, err = b.processFilterNode(root.(*parse.FilterNode))
|
||||
case parse.NodeFunction:
|
||||
q, err = b.processFunctionNode(root.(*parse.FunctionNode))
|
||||
case parse.NodeOperator:
|
||||
q, err = b.processOperatorNode(root.(*parse.OperatorNode))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Build builds a specified XPath expressions expr.
|
||||
func Build(expr string) (query.Query, error) {
|
||||
root := parse.Parse(expr)
|
||||
b := &builder{}
|
||||
return b.processNode(root)
|
||||
}
|
||||
295
trunk/goutil/xmlUtil/gxpath/internal/build/expr.go
Normal file
295
trunk/goutil/xmlUtil/gxpath/internal/build/expr.go
Normal file
@@ -0,0 +1,295 @@
|
||||
package build
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
|
||||
"goutil/xmlUtil/gxpath/internal/query"
|
||||
)
|
||||
|
||||
// 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.Query); ok {
|
||||
return nodeSetType
|
||||
}
|
||||
}
|
||||
panic(fmt.Errorf("xpath unknown value type: %v", v.Kind()))
|
||||
}
|
||||
|
||||
type logical func(query.Iterator, string, interface{}, interface{}) bool
|
||||
|
||||
var logicalFuncs = [][]logical{
|
||||
[]logical{cmpBoolean_Boolean, nil, nil, nil},
|
||||
[]logical{nil, cmpNumeric_Numeric, cmpNumeric_String, cmpNumeric_NodeSet},
|
||||
[]logical{nil, cmpString_Numeric, cmpString_String, cmpString_NodeSet},
|
||||
[]logical{nil, cmpNodeSet_Numeric, cmpNodeSet_String, cmpNodeSet_NodeSet},
|
||||
}
|
||||
|
||||
// number vs number
|
||||
func cmpNumberNumberF(op string, a, b float64) bool {
|
||||
switch op {
|
||||
case "=":
|
||||
return a == b
|
||||
case ">":
|
||||
return a > b
|
||||
case "<":
|
||||
return a < b
|
||||
case ">=":
|
||||
return a >= b
|
||||
case "<=":
|
||||
return a <= b
|
||||
case "!=":
|
||||
return a != b
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// string vs string
|
||||
func cmpStringStringF(op string, a, b string) bool {
|
||||
switch op {
|
||||
case "=":
|
||||
return a == b
|
||||
case ">":
|
||||
return a > b
|
||||
case "<":
|
||||
return a < b
|
||||
case ">=":
|
||||
return a >= b
|
||||
case "<=":
|
||||
return a <= b
|
||||
case "!=":
|
||||
return a != b
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func cmpBooleanBooleanF(op string, a, b bool) bool {
|
||||
switch op {
|
||||
case "or":
|
||||
return a || b
|
||||
case "and":
|
||||
return a && b
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func cmpNumeric_Numeric(t query.Iterator, op string, m, n interface{}) bool {
|
||||
a := m.(float64)
|
||||
b := n.(float64)
|
||||
return cmpNumberNumberF(op, a, b)
|
||||
}
|
||||
|
||||
func cmpNumeric_String(t query.Iterator, op string, m, n interface{}) bool {
|
||||
a := m.(float64)
|
||||
b := n.(string)
|
||||
num, err := strconv.ParseFloat(b, 64)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return cmpNumberNumberF(op, a, num)
|
||||
}
|
||||
|
||||
func cmpNumeric_NodeSet(t query.Iterator, op string, m, n interface{}) bool {
|
||||
a := m.(float64)
|
||||
b := n.(query.Query)
|
||||
|
||||
for {
|
||||
node := b.Select(t)
|
||||
if node == nil {
|
||||
break
|
||||
}
|
||||
num, err := strconv.ParseFloat(node.Value(), 64)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if cmpNumberNumberF(op, a, num) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func cmpNodeSet_Numeric(t query.Iterator, op string, m, n interface{}) bool {
|
||||
a := m.(query.Query)
|
||||
b := n.(float64)
|
||||
for {
|
||||
node := a.Select(t)
|
||||
if node == nil {
|
||||
break
|
||||
}
|
||||
num, err := strconv.ParseFloat(node.Value(), 64)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if cmpNumberNumberF(op, num, b) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func cmpNodeSet_String(t query.Iterator, op string, m, n interface{}) bool {
|
||||
a := m.(query.Query)
|
||||
b := n.(string)
|
||||
for {
|
||||
node := a.Select(t)
|
||||
if node == nil {
|
||||
break
|
||||
}
|
||||
if cmpStringStringF(op, b, node.Value()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func cmpNodeSet_NodeSet(t query.Iterator, op string, m, n interface{}) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func cmpString_Numeric(t query.Iterator, op string, m, n interface{}) bool {
|
||||
a := m.(string)
|
||||
b := n.(float64)
|
||||
num, err := strconv.ParseFloat(a, 64)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return cmpNumberNumberF(op, b, num)
|
||||
}
|
||||
|
||||
func cmpString_String(t query.Iterator, op string, m, n interface{}) bool {
|
||||
a := m.(string)
|
||||
b := n.(string)
|
||||
return cmpStringStringF(op, a, b)
|
||||
}
|
||||
|
||||
func cmpString_NodeSet(t query.Iterator, op string, m, n interface{}) bool {
|
||||
a := m.(string)
|
||||
b := n.(query.Query)
|
||||
for {
|
||||
node := b.Select(t)
|
||||
if node == nil {
|
||||
break
|
||||
}
|
||||
if cmpStringStringF(op, a, node.Value()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func cmpBoolean_Boolean(t query.Iterator, op string, m, n interface{}) bool {
|
||||
a := m.(bool)
|
||||
b := n.(bool)
|
||||
return cmpBooleanBooleanF(op, a, b)
|
||||
}
|
||||
|
||||
// eqFunc is an `=` operator.
|
||||
func eqFunc(t query.Iterator, m, n interface{}) interface{} {
|
||||
t1 := getValueType(m)
|
||||
t2 := getValueType(n)
|
||||
return logicalFuncs[t1][t2](t, "=", m, n)
|
||||
}
|
||||
|
||||
// gtFunc is an `>` operator.
|
||||
func gtFunc(t query.Iterator, m, n interface{}) interface{} {
|
||||
t1 := getValueType(m)
|
||||
t2 := getValueType(n)
|
||||
return logicalFuncs[t1][t2](t, ">", m, n)
|
||||
}
|
||||
|
||||
// geFunc is an `>=` operator.
|
||||
func geFunc(t query.Iterator, m, n interface{}) interface{} {
|
||||
t1 := getValueType(m)
|
||||
t2 := getValueType(n)
|
||||
return logicalFuncs[t1][t2](t, ">=", m, n)
|
||||
}
|
||||
|
||||
// ltFunc is an `<` operator.
|
||||
func ltFunc(t query.Iterator, m, n interface{}) interface{} {
|
||||
t1 := getValueType(m)
|
||||
t2 := getValueType(n)
|
||||
return logicalFuncs[t1][t2](t, "<", m, n)
|
||||
}
|
||||
|
||||
// leFunc is an `<=` operator.
|
||||
func leFunc(t query.Iterator, m, n interface{}) interface{} {
|
||||
t1 := getValueType(m)
|
||||
t2 := getValueType(n)
|
||||
return logicalFuncs[t1][t2](t, "<=", m, n)
|
||||
}
|
||||
|
||||
// neFunc is an `!=` operator.
|
||||
func neFunc(t query.Iterator, m, n interface{}) interface{} {
|
||||
t1 := getValueType(m)
|
||||
t2 := getValueType(n)
|
||||
return logicalFuncs[t1][t2](t, "!=", m, n)
|
||||
}
|
||||
|
||||
// orFunc is an `or` operator.
|
||||
var orFunc = func(t query.Iterator, m, n interface{}) interface{} {
|
||||
t1 := getValueType(m)
|
||||
t2 := getValueType(n)
|
||||
return logicalFuncs[t1][t2](t, "or", m, n)
|
||||
}
|
||||
|
||||
func numericExpr(m, n interface{}, cb func(float64, float64) float64) float64 {
|
||||
typ := reflect.TypeOf(float64(0))
|
||||
a := reflect.ValueOf(m).Convert(typ)
|
||||
b := reflect.ValueOf(n).Convert(typ)
|
||||
return cb(a.Float(), b.Float())
|
||||
}
|
||||
|
||||
// plusFunc is an `+` operator.
|
||||
var plusFunc = func(m, n interface{}) interface{} {
|
||||
return numericExpr(m, n, func(a, b float64) float64 {
|
||||
return a + b
|
||||
})
|
||||
}
|
||||
|
||||
// minusFunc is an `-` operator.
|
||||
var minusFunc = func(m, n interface{}) interface{} {
|
||||
return numericExpr(m, n, func(a, b float64) float64 {
|
||||
return a - b
|
||||
})
|
||||
}
|
||||
|
||||
// mulFunc is an `*` operator.
|
||||
var mulFunc = func(m, n interface{}) interface{} {
|
||||
return numericExpr(m, n, func(a, b float64) float64 {
|
||||
return a * b
|
||||
})
|
||||
}
|
||||
|
||||
// divFunc is an `DIV` operator.
|
||||
var divFunc = func(m, n interface{}) interface{} {
|
||||
return numericExpr(m, n, func(a, b float64) float64 {
|
||||
return a / b
|
||||
})
|
||||
}
|
||||
|
||||
// modFunc is an 'MOD' operator.
|
||||
var modFunc = func(m, n interface{}) interface{} {
|
||||
return numericExpr(m, n, func(a, b float64) float64 {
|
||||
return float64(int(a) % int(b))
|
||||
})
|
||||
}
|
||||
160
trunk/goutil/xmlUtil/gxpath/internal/build/func.go
Normal file
160
trunk/goutil/xmlUtil/gxpath/internal/build/func.go
Normal file
@@ -0,0 +1,160 @@
|
||||
package build
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"goutil/xmlUtil/gxpath/internal/query"
|
||||
"goutil/xmlUtil/gxpath/xpath"
|
||||
)
|
||||
|
||||
func predicate(q query.Query) func(xpath.NodeNavigator) bool {
|
||||
type Predicater interface {
|
||||
Test(xpath.NodeNavigator) bool
|
||||
}
|
||||
if p, ok := q.(Predicater); ok {
|
||||
return p.Test
|
||||
}
|
||||
return func(xpath.NodeNavigator) bool { return true }
|
||||
}
|
||||
|
||||
// positionFunc is a XPath Node Set functions postion().
|
||||
var positionFunc = func(q query.Query, t query.Iterator) interface{} {
|
||||
var (
|
||||
count = 1
|
||||
node = t.Current()
|
||||
)
|
||||
test := predicate(q)
|
||||
for node.MoveToPrevious() {
|
||||
if test(node) {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return float64(count)
|
||||
}
|
||||
|
||||
// lastFunc is a XPath Node Set functions last().
|
||||
var lastFunc = func(q query.Query, t query.Iterator) interface{} {
|
||||
var (
|
||||
count = 0
|
||||
node = t.Current()
|
||||
)
|
||||
node.MoveToFirst()
|
||||
test := predicate(q)
|
||||
for {
|
||||
if test(node) {
|
||||
count++
|
||||
}
|
||||
if !node.MoveToNext() {
|
||||
break
|
||||
}
|
||||
}
|
||||
return float64(count)
|
||||
}
|
||||
|
||||
// countFunc is a XPath Node Set functions count(node-set).
|
||||
var countFunc = func(q query.Query, t query.Iterator) interface{} {
|
||||
var (
|
||||
count = 0
|
||||
node = t.Current()
|
||||
)
|
||||
node.MoveToFirst()
|
||||
test := predicate(q)
|
||||
for {
|
||||
if test(node) {
|
||||
count++
|
||||
}
|
||||
if !node.MoveToNext() {
|
||||
break
|
||||
}
|
||||
}
|
||||
return float64(count)
|
||||
}
|
||||
|
||||
// nameFunc is a XPath functions name([node-set]).
|
||||
var nameFunc = func(q query.Query, t query.Iterator) interface{} {
|
||||
return t.Current().LocalName()
|
||||
}
|
||||
|
||||
// startwithFunc is a XPath functions starts-with(string, string).
|
||||
type startwithFunc struct {
|
||||
arg1, arg2 query.Query
|
||||
}
|
||||
|
||||
func (s *startwithFunc) do(q query.Query, t query.Iterator) interface{} {
|
||||
var (
|
||||
m, n string
|
||||
ok bool
|
||||
)
|
||||
switch typ := s.arg1.Evaluate(t).(type) {
|
||||
case string:
|
||||
m = typ
|
||||
case query.Query:
|
||||
node := typ.Select(t)
|
||||
if node == nil {
|
||||
return false
|
||||
}
|
||||
m = node.Value()
|
||||
default:
|
||||
panic(errors.New("starts-with() function argument type must be string"))
|
||||
}
|
||||
n, ok = s.arg2.Evaluate(t).(string)
|
||||
if !ok {
|
||||
panic(errors.New("starts-with() function argument type must be string"))
|
||||
}
|
||||
return strings.HasPrefix(m, n)
|
||||
}
|
||||
|
||||
// normalizespaceFunc is XPath functions normalize-space(string?)
|
||||
var normalizespaceFunc = func(q query.Query, t query.Iterator) interface{} {
|
||||
var m string
|
||||
switch typ := q.Evaluate(t).(type) {
|
||||
case string:
|
||||
m = typ
|
||||
case query.Query:
|
||||
node := typ.Select(t)
|
||||
if node == nil {
|
||||
return false
|
||||
}
|
||||
m = node.Value()
|
||||
}
|
||||
return strings.TrimSpace(m)
|
||||
}
|
||||
|
||||
// substringFunc is XPath functions substring function returns a part of a given string.
|
||||
type substringFunc struct {
|
||||
arg1, arg2, arg3 query.Query
|
||||
}
|
||||
|
||||
func (f *substringFunc) do(q query.Query, t query.Iterator) interface{} {
|
||||
var m string
|
||||
switch typ := f.arg1.Evaluate(t).(type) {
|
||||
case string:
|
||||
m = typ
|
||||
case query.Query:
|
||||
node := typ.Select(t)
|
||||
if node == nil {
|
||||
return false
|
||||
}
|
||||
m = node.Value()
|
||||
}
|
||||
|
||||
var start, length float64
|
||||
var ok bool
|
||||
|
||||
if start, ok = f.arg2.Evaluate(t).(float64); !ok {
|
||||
panic(errors.New("substring() function first argument type must be int"))
|
||||
}
|
||||
if f.arg3 != nil {
|
||||
if length, ok = f.arg3.Evaluate(t).(float64); !ok {
|
||||
panic(errors.New("substring() function second argument type must be int"))
|
||||
}
|
||||
}
|
||||
if (len(m) - int(start)) < int(length) {
|
||||
panic(errors.New("substring() function start and length argument out of range"))
|
||||
}
|
||||
if length > 0 {
|
||||
return m[int(start):int(length+start)]
|
||||
}
|
||||
return m[int(start):]
|
||||
}
|
||||
Reference in New Issue
Block a user