view src/clojure/contrib/prxml.clj @ 10:ef7dbbd6452c

added clojure source goodness
author Robert McIntyre <rlm@mit.edu>
date Sat, 21 Aug 2010 06:25:44 -0400
parents
children
line wrap: on
line source
1 ;;; prxml.clj -- compact syntax for generating XML
3 ;; by Stuart Sierra, http://stuartsierra.com/
4 ;; March 29, 2009
6 ;; Copyright (c) 2009 Stuart Sierra. All rights reserved. The use and
7 ;; distribution terms for this software are covered by the Eclipse
8 ;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
9 ;; which can be found in the file epl-v10.html at the root of this
10 ;; distribution. By using this software in any fashion, you are
11 ;; agreeing to be bound by the terms of this license. You must not
12 ;; remove this notice, or any other, from this software.
15 ;; Change Log
16 ;;
17 ;; March 29, 2009: added *prxml-indent*
18 ;;
19 ;; January 4, 2009: initial version
22 ;; See function "prxml" at the bottom of this file for documentation.
25 (ns
26 ^{:author "Stuart Sierra",
27 :doc "Compact syntax for generating XML. See the documentation of \"prxml\"
28 for details."}
29 clojure.contrib.prxml
30 (:use [clojure.contrib.string :only (escape as-str)]))
32 (def
33 ^{:doc "If true, empty tags will have a space before the closing />"}
34 *html-compatible* false)
36 (def
37 ^{:doc "The number of spaces to indent sub-tags. nil for no indent
38 and no extra line-breaks."}
39 *prxml-indent* nil)
41 (def ^{:private true} *prxml-tag-depth* 0)
43 (def ^{:private true} print-xml) ; forward declaration
45 (defn- escape-xml [s]
46 (escape {\< "&lt;"
47 \> "&gt;"
48 \& "&amp;"
49 \' "&apos;"
50 \" "&quot;"} s))
52 (defn- prxml-attribute [name value]
53 (print " ")
54 (print (as-str name))
55 (print "=\"")
56 (print (escape-xml (str value)))
57 (print "\""))
59 (defmulti ^{:private true} print-xml-tag (fn [tag attrs content] tag))
61 (defmethod print-xml-tag :raw! [tag attrs contents]
62 (doseq [c contents] (print c)))
64 (defmethod print-xml-tag :comment! [tag attrs contents]
65 (print "<!-- ")
66 (doseq [c contents] (print c))
67 (print " -->"))
69 (defmethod print-xml-tag :decl! [tag attrs contents]
70 (let [attrs (merge {:version "1.0" :encoding "UTF-8"}
71 attrs)]
72 ;; Must enforce ordering of pseudo-attributes:
73 (print "<?xml version=\"")
74 (print (:version attrs))
75 (print "\" encoding=\"")
76 (print (:encoding attrs))
77 (print "\"")
78 (when (:standalone attrs)
79 (print " standalone=\"")
80 (print (:standalone attrs))
81 (print "\""))
82 (print "?>")))
84 (defmethod print-xml-tag :cdata! [tag attrs contents]
85 (print "<![CDATA[")
86 (doseq [c contents] (print c))
87 (print "]]>"))
89 (defmethod print-xml-tag :doctype! [tag attrs contents]
90 (print "<!DOCTYPE ")
91 (doseq [c contents] (print c))
92 (print ">"))
94 (defmethod print-xml-tag :default [tag attrs contents]
95 (let [tag-name (as-str tag)]
96 (when *prxml-indent*
97 (newline)
98 (dotimes [n (* *prxml-tag-depth* *prxml-indent*)] (print " ")))
99 (print "<")
100 (print tag-name)
101 (doseq [[name value] attrs]
102 (prxml-attribute name value))
103 (if (seq contents)
104 (do ;; not an empty tag
105 (print ">")
106 (if (every? string? contents)
107 ;; tag only contains strings:
108 (do (doseq [c contents] (print-xml c))
109 (print "</") (print tag-name) (print ">"))
110 ;; tag contains sub-tags:
111 (do (binding [*prxml-tag-depth* (inc *prxml-tag-depth*)]
112 (doseq [c contents] (print-xml c)))
113 (when *prxml-indent*
114 (newline)
115 (dotimes [n (* *prxml-tag-depth* *prxml-indent*)] (print " ")))
116 (print "</") (print tag-name) (print ">"))))
117 ;; empty tag:
118 (print (if *html-compatible* " />" "/>")))))
121 (defmulti ^{:private true} print-xml class)
123 (defmethod print-xml clojure.lang.IPersistentVector [x]
124 (let [[tag & contents] x
125 [attrs content] (if (map? (first contents))
126 [(first contents) (rest contents)]
127 [{} contents])]
128 (print-xml-tag tag attrs content)))
130 (defmethod print-xml clojure.lang.ISeq [x]
131 ;; Recurse into sequences, so we can use (map ...) inside prxml.
132 (doseq [c x] (print-xml c)))
134 (defmethod print-xml clojure.lang.Keyword [x]
135 (print-xml-tag x {} nil))
137 (defmethod print-xml String [x]
138 (print (escape-xml x)))
140 (defmethod print-xml nil [x])
142 (defmethod print-xml :default [x]
143 (print x))
146 (defn prxml
147 "Print XML to *out*. Vectors become XML tags: the first item is the
148 tag name; optional second item is a map of attributes.
150 Sequences are processed recursively, so you can use map and other
151 sequence functions inside prxml.
153 (prxml [:p {:class \"greet\"} [:i \"Ladies & gentlemen\"]])
154 ; => <p class=\"greet\"><i>Ladies &amp; gentlemen</i></p>
156 PSEUDO-TAGS: some keywords have special meaning:
158 :raw! do not XML-escape contents
159 :comment! create an XML comment
160 :decl! create an XML declaration, with attributes
161 :cdata! create a CDATA section
162 :doctype! create a DOCTYPE!
164 (prxml [:p [:raw! \"<i>here & gone</i>\"]])
165 ; => <p><i>here & gone</i></p>
167 (prxml [:decl! {:version \"1.1\"}])
168 ; => <?xml version=\"1.1\" encoding=\"UTF-8\"?>"
169 [& args]
170 (doseq [arg args] (print-xml arg)))