-
Notifications
You must be signed in to change notification settings - Fork 69
/
pu_map.cljc
111 lines (93 loc) · 3.96 KB
/
pu_map.cljc
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
; Copyright (c) Rich Hickey and contributors. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.data.xml.pu-map
"Provides a bidirectional mapping for keeping track of prefix->uri mappings in xml namespaces.
This has the semantics of a basic key -> multiple values map + two special features, both of which are dictated by the xml standard:
- instead of a special dissoc, there is assoc to empty string or nil
- there are two fixed, unique mappings:
- \"xml\" <-> [\"http://www.w3.org/2000/xmlns/\"]
- \"xmlns\" <-> [\"http://www.w3.org/XML/1998/namespace\"]"
(:require [clojure.data.xml.name :as name]
[clojure.string :as str]
[clojure.core :as core])
(:refer-clojure :exclude [assoc! dissoc! transient persistent! get assoc merge]))
(def prefix-map :p->u)
(def uri-map :u->ps)
;; TODO replace this with a deftype for memory savings
(def EMPTY {:u->ps {name/xml-uri ["xml"]
name/xmlns-uri ["xmlns"]}
:p->u {"xml" name/xml-uri
"xmlns" name/xmlns-uri}})
;; TODO implement valid? with internal consistency check
(defn transient [pu]
(let [{:keys [u->ps p->u] :as pu*}
(or pu EMPTY)]
(assert (and u->ps p->u) (str "Not a pu-map " (pr-str pu*)))
(core/assoc! (core/transient {})
:p->u (core/transient p->u)
:u->ps (core/transient u->ps))))
(defn persistent! [put]
(core/persistent!
(core/assoc! put
:p->u (core/persistent! (core/get put :p->u))
:u->ps (core/persistent! (core/get put :u->ps)))))
(defn- assoc-uri! [u->ps uri prefix]
(core/assoc! u->ps uri
(if-let [ps (core/get u->ps uri)]
(conj ps prefix)
[prefix])))
(defn- dissoc-uri! [u->ps uri prefix]
(if-let [ps (seq (remove #{prefix} (core/get u->ps uri)))]
(core/assoc! u->ps uri (vec ps))
(core/dissoc! u->ps uri)))
(defn assoc! [{:as put :keys [p->u u->ps]} prefix uri]
(name/legal-xmlns-binding! prefix uri)
(let [prefix* (str prefix)
prev-uri (core/get p->u prefix*)]
(core/assoc! put
:p->u (if (str/blank? uri)
(core/dissoc! p->u prefix*)
(core/assoc! p->u prefix* uri))
:u->ps (if (str/blank? uri)
(dissoc-uri! u->ps prev-uri prefix*)
(cond
(= uri prev-uri) u->ps
(not prev-uri) (assoc-uri! u->ps uri prefix*)
:else (-> u->ps
(dissoc-uri! prev-uri prefix*)
(assoc-uri! uri prefix*)))))))
(defn get [{:keys [p->u]} prefix]
(core/get p->u (str prefix)))
(defn get-prefixes [{:keys [u->ps]} uri]
(core/get u->ps uri))
(def get-prefix (comp first get-prefixes))
(defn assoc [put & {:as kvs}]
(persistent!
(reduce-kv assoc! (transient put) kvs)))
(defn reduce-diff
"A high-performance diffing operation, that reduces f over changed and removed prefixes"
[f s
{ppu :p->u}
{pu :p->u}]
(let [s (reduce-kv (fn [s p _]
(if (contains? pu p)
s (f s p "")))
s ppu)
s (reduce-kv (fn [s p u]
(if (= u (core/get ppu p))
s (f s p u)))
s pu)]
s))
(defn merge-prefix-map
"Merge a prefix map into pu-map"
[pu pm]
(persistent! (reduce-kv assoc! (transient pu) pm)))
(defn merge
"Merge two pu-maps, left to right"
[pu {:keys [:p->u]}]
(merge-prefix-map pu p->u))