-
Notifications
You must be signed in to change notification settings - Fork 93
/
xpath.clj
105 lines (80 loc) · 1.99 KB
/
xpath.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
(ns ^:no-doc etaoin.impl.xpath
"A special module to work with XPath language."
(:require [clojure.string :as string]))
(set! *warn-on-reflection* true)
(def Q \")
(defn node-by-text
[text]
(format ".//text()[contains(., %s%s%s)]/.." Q text Q))
(defmulti to-str type)
(defmethod to-str clojure.lang.Keyword
[x]
(name x))
(defmethod to-str String
[x] x)
(defmethod to-str :default
[x]
(str x))
(defmulti clause first)
(defn node-contains
[what text]
(format "[contains(%s, %s%s%s)]" (to-str what) Q (to-str text) Q))
(defn node-equals
[what text]
(format "[%s=%s%s%s]" (to-str what) Q (to-str text) Q))
(defn node-boolean
[what bool]
(if bool
(format "[%s=true()]" (to-str what))
(format "[%s=false()]" (to-str what))))
(defn node-index
[idx]
(format "[%d]" idx))
(defn node-join
[nodes]
(string/join "" nodes))
(defmethod clause :default
[[attr text]]
(node-equals (format "@%s" (to-str attr)) text))
(defmethod clause :fn/text
[[_ text]]
(node-equals "text()" text))
(defmethod clause :fn/has-text
[[_ text]]
(node-contains "text()" text))
(defmethod clause :fn/has-string
[[_ text]]
(node-contains "string()" text))
(defmethod clause :fn/has-class
[[_ class]]
(node-contains "@class" class))
(defmethod clause :fn/has-classes
[[_ classes]]
(node-join
(for [class classes]
(node-contains "@class" class))))
(defmethod clause :fn/disabled
[[_ bool]]
(node-boolean "@disabled" bool))
(defmethod clause :fn/enabled
[[_ bool]]
(node-boolean "@enabled" bool))
(defmethod clause :index
[[_ idx]]
(if idx
(node-index idx)
""))
(defmethod clause :fn/link
[[_ text]]
(node-contains "@href" text))
(defn pop-map
[m k]
[(get m k) (dissoc m k)])
(defn expand
[q]
(let [[tag q] (pop-map q :tag)
tag (or tag :*)
idx-key :index
[index q] (pop-map q idx-key)
nodes (concat (into [] q) {idx-key index})]
(node-join (concat [".//" (to-str tag)] (map clause nodes)))))