/
validator.clj
126 lines (108 loc) · 4.67 KB
/
validator.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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
(ns selmer.validator
(:use selmer.tags
selmer.filters
selmer.util
[clojure.set :only [difference]]
[clojure.java.io :only [reader]]))
(def error-template
(slurp (get-resource "selmer-error-template.html")))
(def validate? (atom true))
(defn validate-on! [] (reset! validate? true))
(defn validate-off! [] (reset! validate? false))
(defn format-tag [{:keys [tag-name tag-value tag-type args]}]
(condp = tag-type
:expr (str *tag-open* *tag-second* " " (name tag-name) " " (if args (str (clojure.string/join args) " ")) *tag-second* *tag-close*)
:filter (str *tag-open* *filter-open* (name tag-value) *filter-close* *tag-close*)
(str tag-name " " tag-value " " tag-type " " args)))
(defn validation-error
([error tag line template]
(validation-error
(str error
(if tag (str " " (format-tag tag)))
(if line (str " on line " line))
(if template (str " for template " template)))
error line [{:tag tag :line line}] template))
([long-error short-error line error-tags template]
(throw
(ex-info long-error
{:type :selmer-validation-error
:error short-error
:error-template error-template
:line line
:template template
:validation-errors
(for [error error-tags]
(update-in error [:tag] format-tag))}))))
(defn validate-filters [template line {:keys [tag-value] :as tag}]
(let [tag-filters (map
#(-> ^String % (.split ":") first keyword)
(-> tag-value name (.split "\\|") rest))]
(if-not (empty? (difference (set tag-filters) (set (keys @filters))))
(validation-error (str "Unrecognized filter " tag-value " found inside the tag") tag line template))))
(defn close-tags []
(apply concat (vals @closing-tags)))
(defn valide-tag [template line tags {:keys [tag-name args tag-value tag-type] :as tag}]
(condp = tag-type
:expr
(let [last-tag (last tags)
end-tags (get @closing-tags (:tag-name last-tag))]
(doseq [arg args] (validate-filters template line (assoc tag :tag-value arg)))
(cond
(nil? tag-name)
(validation-error "No tag name supplied for the tag" tag line template)
(not-any? #{tag-name} (concat (close-tags) (keys @expr-tags)))
(validation-error "Unrecognized tag found" tag line template)
;; check if we have closing tag
;; handle the case where it's an intermediate tag
;; throw an exception if it doesn't belong to the last open tag
(some #{tag-name} (close-tags))
(let [tags (vec (butlast tags))]
(if (some #{tag-name} end-tags)
(if (not-empty (get @closing-tags tag-name))
(conj tags (assoc tag :line line)) tags)
(validation-error "No closing tag found for the tag" last-tag (:line last-tag) template)))
(not-empty (get @closing-tags tag-name))
(conj tags (assoc tag :line line))
(some #{tag-name} (close-tags))
(validation-error "Found an orphan closing tag" tag line template)
:else tags))
:filter
(do (validate-filters template line tag) tags)))
(defn skip-verbatim-tags [tag-info rdr line template]
(if (= :verbatim (:tag-name tag-info))
(loop [ch (read-char rdr)]
(if ch
(if-not (and
(open-tag? ch rdr)
(= :endverbatim (:tag-name (read-tag-info rdr))))
(recur (read-char rdr)))))
tag-info))
(defn read-tag [rdr line template]
(try
(-> (read-tag-info rdr) (skip-verbatim-tags rdr line template))
(catch Exception ex
(validation-error (str "Error parsing the tag: " (.getMessage ex)) nil line template))))
(defn validate-tags [template]
(with-open [rdr (reader template)]
(loop [tags [], ch (read-char rdr), line 1]
(if ch
(if (open-tag? ch rdr)
(if-let [tag-info (read-tag rdr line template)]
(recur (valide-tag template line tags tag-info) (read-char rdr) line)
(recur tags (read-char rdr) line))
(recur tags (read-char rdr) (if (= \newline ch) (inc line) line)))
tags))))
(defn validate [template]
(when @validate?
(check-template-exists template)
(if-let [orphan-tags (not-empty (validate-tags template))]
(validation-error
(->> orphan-tags
(map (fn [{:keys [tag-name line] :as tag}] (str (format-tag tag) " on line " line)))
(interpose ", ")
doall
(clojure.string/join "The template contains orphan tags: "))
"The template contains orphan tags."
nil
orphan-tags
template))))