1package execxp
2
3import (
4	"encoding/xml"
5	"fmt"
6	"strconv"
7	"strings"
8
9	"github.com/ChrisTrenkamp/goxpath/internal/execxp/findutil"
10	"github.com/ChrisTrenkamp/goxpath/internal/execxp/intfns"
11	"github.com/ChrisTrenkamp/goxpath/internal/xsort"
12	"github.com/ChrisTrenkamp/goxpath/lexer"
13	"github.com/ChrisTrenkamp/goxpath/parser"
14	"github.com/ChrisTrenkamp/goxpath/parser/pathexpr"
15	"github.com/ChrisTrenkamp/goxpath/tree"
16	"github.com/ChrisTrenkamp/goxpath/xconst"
17)
18
19type xpFilt struct {
20	t         tree.Node
21	ctx       tree.Result
22	expr      pathexpr.PathExpr
23	ns        map[string]string
24	ctxPos    int
25	ctxSize   int
26	proxPos   map[int]int
27	fns       map[xml.Name]tree.Wrap
28	variables map[string]tree.Result
29}
30
31type xpExecFn func(*xpFilt, string)
32
33var xpFns = map[lexer.XItemType]xpExecFn{
34	lexer.XItemAbsLocPath:     xfAbsLocPath,
35	lexer.XItemAbbrAbsLocPath: xfAbbrAbsLocPath,
36	lexer.XItemRelLocPath:     xfRelLocPath,
37	lexer.XItemAbbrRelLocPath: xfAbbrRelLocPath,
38	lexer.XItemAxis:           xfAxis,
39	lexer.XItemAbbrAxis:       xfAbbrAxis,
40	lexer.XItemNCName:         xfNCName,
41	lexer.XItemQName:          xfQName,
42	lexer.XItemNodeType:       xfNodeType,
43	lexer.XItemProcLit:        xfProcInstLit,
44	lexer.XItemStrLit:         xfStrLit,
45	lexer.XItemNumLit:         xfNumLit,
46}
47
48func xfExec(f *xpFilt, n *parser.Node) (err error) {
49	for n != nil {
50		if fn, ok := xpFns[n.Val.Typ]; ok {
51			fn(f, n.Val.Val)
52			n = n.Left
53		} else if n.Val.Typ == lexer.XItemPredicate {
54			if err = xfPredicate(f, n.Left); err != nil {
55				return
56			}
57
58			n = n.Right
59		} else if n.Val.Typ == lexer.XItemFunction {
60			if err = xfFunction(f, n); err != nil {
61				return
62			}
63
64			n = n.Right
65		} else if n.Val.Typ == lexer.XItemOperator {
66			lf := xpFilt{
67				t:         f.t,
68				ns:        f.ns,
69				ctx:       f.ctx,
70				ctxPos:    f.ctxPos,
71				ctxSize:   f.ctxSize,
72				proxPos:   f.proxPos,
73				fns:       f.fns,
74				variables: f.variables,
75			}
76			left, err := exec(&lf, n.Left)
77			if err != nil {
78				return err
79			}
80
81			rf := xpFilt{
82				t:         f.t,
83				ns:        f.ns,
84				ctx:       f.ctx,
85				fns:       f.fns,
86				variables: f.variables,
87			}
88			right, err := exec(&rf, n.Right)
89			if err != nil {
90				return err
91			}
92
93			return xfOperator(left, right, f, n.Val.Val)
94		} else if n.Val.Typ == lexer.XItemVariable {
95			if res, ok := f.variables[n.Val.Val]; ok {
96				f.ctx = res
97				return nil
98			}
99			return fmt.Errorf("Invalid variable '%s'", n.Val.Val)
100		} else if string(n.Val.Typ) == "" {
101			n = n.Left
102			//} else {
103			//	return fmt.Errorf("Cannot process " + string(n.Val.Typ))
104		}
105	}
106
107	return
108}
109
110func xfPredicate(f *xpFilt, n *parser.Node) (err error) {
111	res := f.ctx.(tree.NodeSet)
112	newRes := make(tree.NodeSet, 0, len(res))
113
114	for i := range res {
115		pf := xpFilt{
116			t:         f.t,
117			ns:        f.ns,
118			ctxPos:    i,
119			ctxSize:   f.ctxSize,
120			ctx:       tree.NodeSet{res[i]},
121			fns:       f.fns,
122			variables: f.variables,
123		}
124
125		predRes, err := exec(&pf, n)
126		if err != nil {
127			return err
128		}
129
130		ok, err := checkPredRes(predRes, f, res[i])
131		if err != nil {
132			return err
133		}
134
135		if ok {
136			newRes = append(newRes, res[i])
137		}
138	}
139
140	f.proxPos = make(map[int]int)
141	for pos, j := range newRes {
142		f.proxPos[j.Pos()] = pos + 1
143	}
144
145	f.ctx = newRes
146	f.ctxSize = len(newRes)
147
148	return
149}
150
151func checkPredRes(ret tree.Result, f *xpFilt, node tree.Node) (bool, error) {
152	if num, ok := ret.(tree.Num); ok {
153		if float64(f.proxPos[node.Pos()]) == float64(num) {
154			return true, nil
155		}
156		return false, nil
157	}
158
159	if b, ok := ret.(tree.IsBool); ok {
160		return bool(b.Bool()), nil
161	}
162
163	return false, fmt.Errorf("Cannot convert argument to boolean")
164}
165
166func xfFunction(f *xpFilt, n *parser.Node) error {
167	spl := strings.Split(n.Val.Val, ":")
168	var name xml.Name
169	if len(spl) == 1 {
170		name.Local = spl[0]
171	} else {
172		name.Space = f.ns[spl[0]]
173		name.Local = spl[1]
174	}
175	fn, ok := intfns.BuiltIn[name]
176	if !ok {
177		fn, ok = f.fns[name]
178	}
179
180	if ok {
181		args := []tree.Result{}
182		param := n.Left
183
184		for param != nil {
185			pf := xpFilt{
186				t:         f.t,
187				ctx:       f.ctx,
188				ns:        f.ns,
189				ctxPos:    f.ctxPos,
190				ctxSize:   f.ctxSize,
191				fns:       f.fns,
192				variables: f.variables,
193			}
194			res, err := exec(&pf, param.Left)
195			if err != nil {
196				return err
197			}
198
199			args = append(args, res)
200			param = param.Right
201		}
202
203		filt, err := fn.Call(tree.Ctx{NodeSet: f.ctx.(tree.NodeSet), Size: f.ctxSize, Pos: f.ctxPos + 1}, args...)
204		f.ctx = filt
205		return err
206	}
207
208	return fmt.Errorf("Unknown function: %s", n.Val.Val)
209}
210
211var eqOps = map[string]bool{
212	"=":  true,
213	"!=": true,
214}
215
216var booleanOps = map[string]bool{
217	"=":  true,
218	"!=": true,
219	"<":  true,
220	"<=": true,
221	">":  true,
222	">=": true,
223}
224
225var numOps = map[string]bool{
226	"*":   true,
227	"div": true,
228	"mod": true,
229	"+":   true,
230	"-":   true,
231	"=":   true,
232	"!=":  true,
233	"<":   true,
234	"<=":  true,
235	">":   true,
236	">=":  true,
237}
238
239var andOrOps = map[string]bool{
240	"and": true,
241	"or":  true,
242}
243
244func xfOperator(left, right tree.Result, f *xpFilt, op string) error {
245	if booleanOps[op] {
246		lNode, lOK := left.(tree.NodeSet)
247		rNode, rOK := right.(tree.NodeSet)
248		if lOK && rOK {
249			return bothNodeOperator(lNode, rNode, f, op)
250		}
251
252		if lOK {
253			return leftNodeOperator(lNode, right, f, op)
254		}
255
256		if rOK {
257			return rightNodeOperator(left, rNode, f, op)
258		}
259
260		if eqOps[op] {
261			return equalsOperator(left, right, f, op)
262		}
263	}
264
265	if numOps[op] {
266		return numberOperator(left, right, f, op)
267	}
268
269	if andOrOps[op] {
270		return andOrOperator(left, right, f, op)
271	}
272
273	//if op == "|" {
274	return unionOperator(left, right, f, op)
275	//}
276
277	//return fmt.Errorf("Unknown operator " + op)
278}
279
280func xfAbsLocPath(f *xpFilt, val string) {
281	i := f.t
282	for i.GetNodeType() != tree.NtRoot {
283		i = i.GetParent()
284	}
285	f.ctx = tree.NodeSet{i}
286}
287
288func xfAbbrAbsLocPath(f *xpFilt, val string) {
289	i := f.t
290	for i.GetNodeType() != tree.NtRoot {
291		i = i.GetParent()
292	}
293	f.ctx = tree.NodeSet{i}
294	f.expr = abbrPathExpr()
295	find(f)
296}
297
298func xfRelLocPath(f *xpFilt, val string) {
299}
300
301func xfAbbrRelLocPath(f *xpFilt, val string) {
302	f.expr = abbrPathExpr()
303	find(f)
304}
305
306func xfAxis(f *xpFilt, val string) {
307	f.expr.Axis = val
308}
309
310func xfAbbrAxis(f *xpFilt, val string) {
311	f.expr.Axis = xconst.AxisAttribute
312}
313
314func xfNCName(f *xpFilt, val string) {
315	f.expr.Name.Space = val
316}
317
318func xfQName(f *xpFilt, val string) {
319	f.expr.Name.Local = val
320	find(f)
321}
322
323func xfNodeType(f *xpFilt, val string) {
324	f.expr.NodeType = val
325	find(f)
326}
327
328func xfProcInstLit(f *xpFilt, val string) {
329	filt := tree.NodeSet{}
330	for _, i := range f.ctx.(tree.NodeSet) {
331		if i.GetToken().(xml.ProcInst).Target == val {
332			filt = append(filt, i)
333		}
334	}
335	f.ctx = filt
336}
337
338func xfStrLit(f *xpFilt, val string) {
339	f.ctx = tree.String(val)
340}
341
342func xfNumLit(f *xpFilt, val string) {
343	num, _ := strconv.ParseFloat(val, 64)
344	f.ctx = tree.Num(num)
345}
346
347func abbrPathExpr() pathexpr.PathExpr {
348	return pathexpr.PathExpr{
349		Name:     xml.Name{},
350		Axis:     xconst.AxisDescendentOrSelf,
351		NodeType: xconst.NodeTypeNode,
352	}
353}
354
355func find(f *xpFilt) {
356	dupFilt := make(map[int]tree.Node)
357	f.proxPos = make(map[int]int)
358
359	if f.expr.Axis == "" && f.expr.NodeType == "" && f.expr.Name.Space == "" {
360		if f.expr.Name.Local == "." {
361			f.expr = pathexpr.PathExpr{
362				Name:     xml.Name{},
363				Axis:     xconst.AxisSelf,
364				NodeType: xconst.NodeTypeNode,
365			}
366		}
367
368		if f.expr.Name.Local == ".." {
369			f.expr = pathexpr.PathExpr{
370				Name:     xml.Name{},
371				Axis:     xconst.AxisParent,
372				NodeType: xconst.NodeTypeNode,
373			}
374		}
375	}
376
377	f.expr.NS = f.ns
378
379	for _, i := range f.ctx.(tree.NodeSet) {
380		for pos, j := range findutil.Find(i, f.expr) {
381			dupFilt[j.Pos()] = j
382			f.proxPos[j.Pos()] = pos + 1
383		}
384	}
385
386	res := make(tree.NodeSet, 0, len(dupFilt))
387	for _, i := range dupFilt {
388		res = append(res, i)
389	}
390
391	xsort.SortNodes(res)
392
393	f.expr = pathexpr.PathExpr{}
394	f.ctxSize = len(res)
395	f.ctx = res
396}
397