From 96692a402b758222c64aff931cbbd5add73f79bb Mon Sep 17 00:00:00 2001 From: Alan Malloy Date: Wed, 4 Jan 2012 11:41:34 -0800 Subject: [PATCH 001/237] Remove indentation test so we can get a draft-release out --- .../clojure/clojure/data/xml/test_emit.clj | 20 +------------------ 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/modules/xml/src/test/clojure/clojure/data/xml/test_emit.clj b/modules/xml/src/test/clojure/clojure/data/xml/test_emit.clj index 5f5aee1..4679b83 100644 --- a/modules/xml/src/test/clojure/clojure/data/xml/test_emit.clj +++ b/modules/xml/src/test/clojure/clojure/data/xml/test_emit.clj @@ -56,25 +56,7 @@ (is (= expect (with-out-str (xml/emit deep-tree :xml-declaration false)))))) -(deftest indent - (let [input-tree - (with-in-str (str "" - "t2" - "t4t5t6" - "" - " t10t11" - "t13t14" - "") - (xml/parse *in*)) - expect (str "\n" - "\n" - " t2\n" - " t4t5t6\n" - " \n" - " t10t11\n" - " t13t14\n")] - (is (= expect (with-out-str - (xml/emit input-tree :indent 3)))))) +;; TODO add an indentation test once we figure out how to indent portably across JREs (defn emit-char-seq [xml-tree encoding] (let [stream (java.io.ByteArrayOutputStream.)] From 1b8c0bb1eabcb6d27eda31b824de4df53b4b52a0 Mon Sep 17 00:00:00 2001 From: Alan Malloy Date: Wed, 4 Jan 2012 11:42:00 -0800 Subject: [PATCH 002/237] Release version 0.0.1 and add myself as a contributor --- pom.xml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9a81c68..653f318 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ 4.0.0 data.xml.root - 0.0.1-SNAPSHOT + 0.0.1 data.xml.root pom Functions to parse XML into lazy sequences and lazy trees and emit these as text @@ -13,6 +13,11 @@ http://chouser.n01se.net -5 + + Alan Malloy + amalloy@4clojure.com + -8 + From ea0ea1017cae8dc3d36b29d417c2dba8ee5bba42 Mon Sep 17 00:00:00 2001 From: Alan Malloy Date: Wed, 4 Jan 2012 13:50:45 -0800 Subject: [PATCH 003/237] Delete test that was making an incorrect assertion. See github commit notes on e08334d1 for a summary of the issue with this assertion. --- .../test/clojure/clojure/data/xml/test_parse.clj | 13 ------------- pom.xml | 2 +- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/modules/xml/src/test/clojure/clojure/data/xml/test_parse.clj b/modules/xml/src/test/clojure/clojure/data/xml/test_parse.clj index a6498e3..1a2513b 100644 --- a/modules/xml/src/test/clojure/clojure/data/xml/test_parse.clj +++ b/modules/xml/src/test/clojure/clojure/data/xml/test_parse.clj @@ -37,16 +37,3 @@ " t12" (element :g {} "t13") "t14")] (is (= expected (with-in-str input (xml/parse *in*)))) (with-in-str input (is (= expected (xml/lazy-parse *in*)))))) - -(deftest source-seq-release-head - (doseq [func [xml/source-seq xml/lazy-source-seq]] - (let [event1 (atom (xml/event :start-element :foo)) - weak (java.lang.ref.WeakReference. @event1)] - (with-redefs [xml/fill-from-sax - (fn [src strt fill] - (fill @event1) - (fill (xml/event :end-element :foo)))] - (let [more (rest (func nil nil))] - (reset! event1 nil) - (System/gc) - (is (= nil (.get weak)))))))) diff --git a/pom.xml b/pom.xml index 653f318..1b3aa52 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ 4.0.0 data.xml.root - 0.0.1 + 0.0.2-SNAPSHOT data.xml.root pom Functions to parse XML into lazy sequences and lazy trees and emit these as text From ca91651e542afa278f10960170ad8305dc5dfbc5 Mon Sep 17 00:00:00 2001 From: Ryan Senior Date: Fri, 3 Feb 2012 07:17:26 -0600 Subject: [PATCH 004/237] Added a statement to the README on getting your ns setup for data.xml --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 7201efd..cfaaeb1 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,10 @@ Add the following to the `project.clj` dependencies: ## Examples +The examples below assume you have added a `use` for data.xml: + + (use 'clojure.data.xml) + data.xml supports parsing and emitting XML. The parsing functions will read XML from a [Reader](http://docs.oracle.com/javase/6/docs/api/java/io/Reader.html) From edd0aebb992b4a1ad6e0fb6f314540f1fbdda05e Mon Sep 17 00:00:00 2001 From: Ryan Senior Date: Mon, 6 Feb 2012 10:22:46 -0600 Subject: [PATCH 005/237] Fixed an issue with the encoding test on Mac OS X --- src/test/clojure/clojure/data/xml/test_utils.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/clojure/clojure/data/xml/test_utils.clj b/src/test/clojure/clojure/data/xml/test_utils.clj index d945712..64052e4 100644 --- a/src/test/clojure/clojure/data/xml/test_utils.clj +++ b/src/test/clojure/clojure/data/xml/test_utils.clj @@ -12,7 +12,7 @@ (:require [clojure.data.xml :as xml])) (defn test-stream [x] - (java.io.ByteArrayInputStream. (.getBytes x))) + (java.io.ByteArrayInputStream. (.getBytes x "UTF-8"))) (def lazy-parse* (comp xml/parse test-stream)) From 89fe25e8f4c577c5d5b50a4ffcaf0627ea81c350 Mon Sep 17 00:00:00 2001 From: Ryan Senior Date: Thu, 9 Feb 2012 11:02:50 -0600 Subject: [PATCH 006/237] Added a default case to skip processing instructions, doctypes etc --- src/main/clojure/clojure/data/xml.clj | 8 +++----- src/test/clojure/clojure/data/xml/test_parse.clj | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/main/clojure/clojure/data/xml.clj b/src/main/clojure/clojure/data/xml.clj index 02d0772..eba575d 100644 --- a/src/main/clojure/clojure/data/xml.clj +++ b/src/main/clojure/clojure/data/xml.clj @@ -214,12 +214,10 @@ (cons (event :characters nil nil text) (pull-seq sreader)) (recur)) - XMLStreamConstants/COMMENT - (recur) - XMLStreamConstants/SPACE - (recur) XMLStreamConstants/END_DOCUMENT - nil)))) + nil + (recur);; Consume and ignore comments, spaces, processing instructions etc + )))) (defn source-seq "Parses the XML InputSource source using a pull-parser. Returns diff --git a/src/test/clojure/clojure/data/xml/test_parse.clj b/src/test/clojure/clojure/data/xml/test_parse.clj index dfb1271..74d41f2 100644 --- a/src/test/clojure/clojure/data/xml/test_parse.clj +++ b/src/test/clojure/clojure/data/xml/test_parse.clj @@ -59,3 +59,17 @@ "there")))] (is (= expected (lazy-parse* input))))) +(deftest test-parsing-processing-instructions + (let [input " + + With Stuff" + expected (element :ATag {} "With Stuff")] + (is (= expected (parse-str input))))) + +(deftest test-parsing-doctypes + (let [input " +

Heading Stuff

" + expected (element :html {} + (element :h1 {} "Heading Stuff"))] + (is (= expected (parse-str input))))) \ No newline at end of file From e719b588892cc2a455241afe8638226229d204b1 Mon Sep 17 00:00:00 2001 From: Alexander Taggart Date: Sun, 19 Feb 2012 14:30:29 -0800 Subject: [PATCH 007/237] Fix formatting of examples. --- README.md | 132 +++++++++++++++++++++++++++--------------------------- 1 file changed, 67 insertions(+), 65 deletions(-) diff --git a/README.md b/README.md index cfaaeb1..18e3b7d 100644 --- a/README.md +++ b/README.md @@ -34,9 +34,9 @@ more information. For Maven projects, add the following XML in your `pom.xml`'s `` section: - org.clojure - data.xml - 0.0.2-SNAPSHOT + org.clojure + data.xml + 0.0.2-SNAPSHOT ### Leiningen @@ -48,7 +48,7 @@ Add the following to the `project.clj` dependencies: The examples below assume you have added a `use` for data.xml: - (use 'clojure.data.xml) + (use 'clojure.data.xml) data.xml supports parsing and emitting XML. The parsing functions will read XML from a @@ -56,16 +56,17 @@ read XML from a or [InputStream](http://docs.oracle.com/javase/6/docs/api/java/io/InputStream.html). - (let [input-xml (java.io.StringReader. " - The baz value")] - (parse input-xml)) - #clojure.data.xml.Element{:tag :foo, - :attrs {}, - :content (#clojure.data.xml.Element{:tag :bar, - :attrs {}, - :content (#clojure.data.xml.Element{:tag :baz, - :attrs {}, - :content ("The baz value")})})} + (let [input-xml (java.io.StringReader. " + The baz value")] + (parse input-xml)) + + #clojure.data.xml.Element{:tag :foo, + :attrs {}, + :content (#clojure.data.xml.Element{:tag :bar, + :attrs {}, + :content (#clojure.data.xml.Element{:tag :baz, + :attrs {}, + :content ("The baz value")})})} The data is returned as defrecords and can be manipulated using the normal clojure data structure functions. @@ -74,84 +75,85 @@ XML elements can be created using the typical defrecord constructor functions or the element function used below, and written using a [java.io.Writer](http://docs.oracle.com/javase/6/docs/api/java/io/Writer.html).: - (let [tags (element :foo {:foo-attr "foo value"} - (element :bar {:bar-attr "bar value"} - (element :baz {} "The baz value")))] - (with-open [out-file (java.io.FileWriter. "/tmp/foo.xml")] - (emit tags out-file))) + (let [tags (element :foo {:foo-attr "foo value"} + (element :bar {:bar-attr "bar value"} + (element :baz {} "The baz value")))] + (with-open [out-file (java.io.FileWriter. "/tmp/foo.xml")] + (emit tags out-file))) - ;;-> Writes XML to /tmp/foo.xml + ;;-> Writes XML to /tmp/foo.xml XML can be "round tripped" through the library: - (let [tags (element :foo {:foo-attr "foo value"} - (element :bar {:bar-attr "bar value"} - (element :baz {} "The baz value")))] - (with-open [out-file (java.io.FileWriter. "/tmp/foo.xml")] - (emit tags out-file)) - (with-open [input (java.io.FileInputStream. "/tmp/foo.xml")] - (parse input))) + (let [tags (element :foo {:foo-attr "foo value"} + (element :bar {:bar-attr "bar value"} + (element :baz {} "The baz value")))] + (with-open [out-file (java.io.FileWriter. "/tmp/foo.xml")] + (emit tags out-file)) + (with-open [input (java.io.FileInputStream. "/tmp/foo.xml")] + (parse input))) - #clojure.data.xml.Element{:tag :foo, :attrs {:foo-attr "foo value"}...} + #clojure.data.xml.Element{:tag :foo, :attrs {:foo-attr "foo value"}...} There are also some string based functions that are useful for debugging. - (let [tags (element :foo {:foo-attr "foo value"} - (element :bar {:bar-attr "bar value"} - (element :baz {} "The baz value")))] - (= tags (parse-str (emit-str tags)))) - - ;;-> true + (let [tags (element :foo {:foo-attr "foo value"} + (element :bar {:bar-attr "bar value"} + (element :baz {} "The baz value")))] + (= tags (parse-str (emit-str tags)))) + + true Indentation is supported, but should be treated as a debugging feature as it's likely to be pretty slow: - (print (indent-str (element :foo {:foo-attr "foo value"} - (element :bar {:bar-attr "bar value"} - (element :baz {} "The baz value1") - (element :baz {} "The baz value2") - (element :baz {} "The baz value3"))))) - - - - - The baz value1 - The baz value2 - The baz value3 - - + (print (indent-str (element :foo {:foo-attr "foo value"} + (element :bar {:bar-attr "bar value"} + (element :baz {} "The baz value1") + (element :baz {} "The baz value2") + (element :baz {} "The baz value3"))))) + + + + + The baz value1 + The baz value2 + The baz value3 + + CDATA can be emitted: - (emit-str (element :foo {} - (cdata ""))) - "]]>" + (emit-str (element :foo {} + (cdata ""))) + + "]]>" But will be read as regular character data: - (parse-str (emit-str (element :foo {} - (cdata "")))) - #clojure.data.xml.Element{:tag :foo, :attrs {}, :content ("")} + (parse-str (emit-str (element :foo {} + (cdata "")))) + + #clojure.data.xml.Element{:tag :foo, :attrs {}, :content ("")} Comments can also be emitted: - (emit-str (element :foo {} - (xml-comment "Just a goes here") - (element :bar {} "and another element"))) + (emit-str (element :foo {} + (xml-comment "Just a goes here") + (element :bar {} "and another element"))) - "and another element" + "and another element" But are ignored when read: - (emit-str - (parse-str - (emit-str (element :foo {} - (xml-comment "Just a goes here") - (element :bar {} "and another element"))))) + (emit-str + (parse-str + (emit-str (element :foo {} + (xml-comment "Just a goes here") + (element :bar {} "and another element"))))) - "and another element" + "and another element" ## License From 570959b04825253d50d18e476693b18d370a9b13 Mon Sep 17 00:00:00 2001 From: "Hudson @ build.clojure.org" Date: Fri, 24 Feb 2012 15:43:51 -0600 Subject: [PATCH 008/237] [maven-release-plugin] prepare release data.xml-0.0.3 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d564ce2..ce75dcd 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ 4.0.0 data.xml - 0.0.3-SNAPSHOT + 0.0.3 data.xml jar Functions to parse XML into lazy sequences and lazy trees and emit these as text From 2f6e6f0974e5afe5ba40b49f0b86d3e06cb6539f Mon Sep 17 00:00:00 2001 From: "Hudson @ build.clojure.org" Date: Fri, 24 Feb 2012 15:43:51 -0600 Subject: [PATCH 009/237] [maven-release-plugin] prepare for next development iteration --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ce75dcd..9b9bf35 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ 4.0.0 data.xml - 0.0.3 + 0.0.4-SNAPSHOT data.xml jar Functions to parse XML into lazy sequences and lazy trees and emit these as text From 1cf6cf853255e7a6cc65f8d6522b38c8f3e6ed8b Mon Sep 17 00:00:00 2001 From: Justin Kramer Date: Fri, 10 Feb 2012 10:07:27 -0500 Subject: [PATCH 010/237] keyword options for source-seq, parse, and parse-str Signed-off-by: Ryan Senior --- src/main/clojure/clojure/data/xml.clj | 31 ++++++++++++++----- .../clojure/clojure/data/xml/test_parse.clj | 8 ++++- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/src/main/clojure/clojure/data/xml.clj b/src/main/clojure/clojure/data/xml.clj index eba575d..d5209be 100644 --- a/src/main/clojure/clojure/data/xml.clj +++ b/src/main/clojure/clojure/data/xml.clj @@ -219,12 +219,29 @@ (recur);; Consume and ignore comments, spaces, processing instructions etc )))) +(def ^{:private true} xml-input-factory-props + {:allocator javax.xml.stream.XMLInputFactory/ALLOCATOR + :coalescing javax.xml.stream.XMLInputFactory/IS_COALESCING + :namespace-aware javax.xml.stream.XMLInputFactory/IS_NAMESPACE_AWARE + :replacing-entity-references javax.xml.stream.XMLInputFactory/IS_REPLACING_ENTITY_REFERENCES + :supporting-external-entities javax.xml.stream.XMLInputFactory/IS_SUPPORTING_EXTERNAL_ENTITIES + :validating javax.xml.stream.XMLInputFactory/IS_VALIDATING + :reporter javax.xml.stream.XMLInputFactory/REPORTER + :resolver javax.xml.stream.XMLInputFactory/RESOLVER + :support-dtd javax.xml.stream.XMLInputFactory/SUPPORT_DTD}) + +(defn- new-xml-input-factory [props] + (let [fac (javax.xml.stream.XMLInputFactory/newInstance)] + (doseq [[k v] props + :let [prop (xml-input-factory-props k)]] + (.setProperty fac prop v)) + fac)) + (defn source-seq "Parses the XML InputSource source using a pull-parser. Returns a lazy sequence of Event records." - [s] - (let [fac (doto (javax.xml.stream.XMLInputFactory/newInstance) - (.setProperty javax.xml.stream.XMLInputFactory/IS_COALESCING true)) + [s & {:as props}] + (let [fac (new-xml-input-factory (merge {:coalescing true} props)) sreader (.createXMLStreamReader fac s)] (pull-seq sreader))) @@ -232,14 +249,14 @@ "Convenience function. Parses the source, which can be an InputStream or Reader, and returns a lazy tree of Element records. See lazy-source-seq for finer-grained control." - [source] - (event-tree (source-seq source))) + [source & props] + (event-tree (apply source-seq source props))) (defn parse-str "Parses the passed in string to Clojure data structures" - [s] + [s & props] (let [sr (java.io.StringReader. s)] - (parse sr))) + (apply parse sr props))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; XML Emitting diff --git a/src/test/clojure/clojure/data/xml/test_parse.clj b/src/test/clojure/clojure/data/xml/test_parse.clj index 74d41f2..9a6fca3 100644 --- a/src/test/clojure/clojure/data/xml/test_parse.clj +++ b/src/test/clojure/clojure/data/xml/test_parse.clj @@ -72,4 +72,10 @@

Heading Stuff

" expected (element :html {} (element :h1 {} "Heading Stuff"))] - (is (= expected (parse-str input))))) \ No newline at end of file + (is (= expected (parse-str input))))) + +(deftest test-coalescing + (let [input ""] + (is (= ["\nfoo bar\n\nbaz\n"] (:content (parse-str input)))) + (is (= ["\nfoo bar\n" "\nbaz\n"] (:content + (parse-str input :coalescing false)))))) \ No newline at end of file From 5671774c5013f13fea3410be6e461a5f4956a7f5 Mon Sep 17 00:00:00 2001 From: Ryan Senior Date: Wed, 14 Mar 2012 11:19:24 -0500 Subject: [PATCH 011/237] Added some comments/docs on the keyword input parsing parameters --- README.md | 6 +++++- src/main/clojure/clojure/data/xml.clj | 15 ++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 18e3b7d..133169b 100644 --- a/README.md +++ b/README.md @@ -69,8 +69,12 @@ or :content ("The baz value")})})} The data is returned as defrecords and can be manipulated using the -normal clojure data structure functions. +normal clojure data structure functions. Additional parsing options +can be passed via key pairs: + (parse-str "" :coalescing false) + #clojure.data.xml.Element{:tag :a, :attrs {}, :content ("\nfoo bar\n" "\nbaz\n")} + XML elements can be created using the typical defrecord constructor functions or the element function used below, and written using a [java.io.Writer](http://docs.oracle.com/javase/6/docs/api/java/io/Writer.html).: diff --git a/src/main/clojure/clojure/data/xml.clj b/src/main/clojure/clojure/data/xml.clj index d5209be..751dc4b 100644 --- a/src/main/clojure/clojure/data/xml.clj +++ b/src/main/clojure/clojure/data/xml.clj @@ -239,21 +239,26 @@ (defn source-seq "Parses the XML InputSource source using a pull-parser. Returns - a lazy sequence of Event records." + a lazy sequence of Event records. Accepts key pairs + with XMLInputFactory options, see http://docs.oracle.com/javase/6/docs/api/javax/xml/stream/XMLInputFactory.html + and xml-input-factory-props for more information. Defaults coalescing true." [s & {:as props}] (let [fac (new-xml-input-factory (merge {:coalescing true} props)) sreader (.createXMLStreamReader fac s)] (pull-seq sreader))) (defn parse - "Convenience function. Parses the source, which can be an - InputStream or Reader, and returns a lazy tree of Element records. - See lazy-source-seq for finer-grained control." + "Parses the source, which can be an + InputStream or Reader, and returns a lazy tree of Element records. Accepts key pairs + with XMLInputFactory options, see http://docs.oracle.com/javase/6/docs/api/javax/xml/stream/XMLInputFactory.html + and xml-input-factory-props for more information. Defaults coalescing true." [source & props] (event-tree (apply source-seq source props))) (defn parse-str - "Parses the passed in string to Clojure data structures" + "Parses the passed in string to Clojure data structures. Accepts key pairs + with XMLInputFactory options, see http://docs.oracle.com/javase/6/docs/api/javax/xml/stream/XMLInputFactory.html + and xml-input-factory-props for more information. Defaults coalescing true." [s & props] (let [sr (java.io.StringReader. s)] (apply parse sr props))) From 2ec7dbb7c09755c21bb4dc7b9013fddb54573fe2 Mon Sep 17 00:00:00 2001 From: Aaron Bedra Date: Sun, 25 Mar 2012 15:53:30 -0500 Subject: [PATCH 012/237] Update README to reflect current version --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 133169b..9981f03 100644 --- a/README.md +++ b/README.md @@ -36,13 +36,13 @@ For Maven projects, add the following XML in your `pom.xml`'s `` s org.clojure data.xml - 0.0.2-SNAPSHOT + 0.0.3 ### Leiningen Add the following to the `project.clj` dependencies: - [org.clojure/data.xml "0.0.2-SNAPSHOT"] + [org.clojure/data.xml "0.0.3"] ## Examples From 755647b4eb114dce0c99f8a93d72ce7fdbdbb495 Mon Sep 17 00:00:00 2001 From: Ryan Senior Date: Tue, 22 May 2012 10:09:21 -0500 Subject: [PATCH 013/237] Fix for emit-element holding onto the head of the lazy-seq of elements (http://dev.clojure.org/jira/browse/DXML-5). --- src/main/clojure/clojure/data/xml.clj | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/clojure/data/xml.clj b/src/main/clojure/clojure/data/xml.clj index 751dc4b..313136d 100644 --- a/src/main/clojure/clojure/data/xml.clj +++ b/src/main/clojure/clojure/data/xml.clj @@ -34,8 +34,10 @@ (.writeAttribute writer (name k) (str v))))) ; Represents a node of an XML tree -(defrecord Element [tag attrs content] - Emit +(defrecord Element [tag attrs content]) + +(extend-protocol Emit + Element (emit-element [e writer] (let [nspace (namespace (:tag e)) qname (name (:tag e)) From 10c8f4952cf119493ca85409276b09dedf16a91a Mon Sep 17 00:00:00 2001 From: Ryan Senior Date: Tue, 22 May 2012 10:52:58 -0500 Subject: [PATCH 014/237] Code cleanup, consistent ns usage, whitespace etc --- src/main/clojure/clojure/data/xml.clj | 4 ++-- .../clojure/clojure/data/xml/test_emit.clj | 22 +++++++++---------- .../clojure/clojure/data/xml/test_parse.clj | 4 ++-- .../clojure/data/xml/test_seq_tree.clj | 6 ++--- .../clojure/clojure/data/xml/test_sexp.clj | 6 ++--- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/main/clojure/clojure/data/xml.clj b/src/main/clojure/clojure/data/xml.clj index 313136d..b18e17f 100644 --- a/src/main/clojure/clojure/data/xml.clj +++ b/src/main/clojure/clojure/data/xml.clj @@ -73,7 +73,7 @@ (Comment. content)) ;=== Parse-related functions === -(defn- seq-tree +(defn seq-tree "Takes a seq of events that logically represents a tree by each event being one of: enter-sub-tree event, exit-sub-tree event, or node event. @@ -320,5 +320,5 @@ [e] (let [^java.io.StringWriter sw (java.io.StringWriter.)] (indent e sw) - (.toString sw)) ) + (.toString sw))) diff --git a/src/test/clojure/clojure/data/xml/test_emit.clj b/src/test/clojure/clojure/data/xml/test_emit.clj index 73de0fa..2a95641 100644 --- a/src/test/clojure/clojure/data/xml/test_emit.clj +++ b/src/test/clojure/clojure/data/xml/test_emit.clj @@ -8,9 +8,9 @@ (ns ^{:doc "Tests for emit to print XML text." :author "Chris Houser"} - clojure.data.xml.test-emit - (:use [clojure.test :only [deftest is are]] - [clojure.data.xml :as xml :only [element cdata xml-comment]] + clojure.data.xml.test-emit + (:use clojure.test + clojure.data.xml [clojure.data.xml.test-utils :only (test-stream lazy-parse*)])) (def deep-tree @@ -33,13 +33,13 @@ " t8t10t11" " t12t13t14" "")] - (is (= expect (xml/emit-str deep-tree))))) + (is (= expect (emit-str deep-tree))))) (deftest mixed-quotes (is (= (str "" "") - (xml/emit-str (element :mixed + (emit-str (element :mixed {:single "'single'quotes'here" :double "\"double\"quotes\"here\""}))))) @@ -49,7 +49,7 @@ (defn emit-char-seq [xml-tree encoding] (with-open [bos (java.io.ByteArrayOutputStream.) stream (java.io.OutputStreamWriter. bos encoding)] - (xml/emit xml-tree stream :encoding encoding) + (emit xml-tree stream :encoding encoding) (.flush stream) (map #(if (pos? %) (char %) %) (.toByteArray bos)))) @@ -67,18 +67,18 @@ (is (thrown? Exception (let [stream (java.io.ByteArrayOutputStream.)] (binding [*out* (java.io.OutputStreamWriter. stream "UTF-8")] - (xml/emit (element :foo) *out* :encoding "ISO-8859-1")))))) + (emit (element :foo) *out* :encoding "ISO-8859-1")))))) (deftest emitting-cdata (is (= (str "" "]]>") - (xml/emit-str (element :cdata-stuff {} + (emit-str (element :cdata-stuff {} (cdata ""))))) ) (deftest emitting-comment (is (= (str "" "comment not here") - (xml/emit-str (element :comment-stuff {} + (emit-str (element :comment-stuff {} "comment " (xml-comment " goes here ") " not here")))) ) @@ -88,11 +88,11 @@ expect (str "\n\n " "\n \n foo\n \n \n\n") sw (java.io.StringWriter.)] - (xml/indent nested-xml sw) + (indent nested-xml sw) (is (= expect (.toString sw))))) (deftest test-indent-str (let [nested-xml (lazy-parse* (str "foo")) expect (str "\n\n " "\n \n foo\n \n \n\n")] - (is (= expect (xml/indent-str nested-xml))))) + (is (= expect (indent-str nested-xml))))) diff --git a/src/test/clojure/clojure/data/xml/test_parse.clj b/src/test/clojure/clojure/data/xml/test_parse.clj index 9a6fca3..e5e5f83 100644 --- a/src/test/clojure/clojure/data/xml/test_parse.clj +++ b/src/test/clojure/clojure/data/xml/test_parse.clj @@ -9,8 +9,8 @@ (ns ^{:doc "Tests for XML parsing functions." :author "Chris Houser"} clojure.data.xml.test-parse - (:use [clojure.test :only [deftest is are]] - [clojure.data.xml :as xml :only [element cdata parse-str]] + (:use clojure.test + clojure.data.xml [clojure.data.xml.test-utils :only [test-stream lazy-parse*]])) (deftest simple diff --git a/src/test/clojure/clojure/data/xml/test_seq_tree.clj b/src/test/clojure/clojure/data/xml/test_seq_tree.clj index 4740624..44fac5d 100644 --- a/src/test/clojure/clojure/data/xml/test_seq_tree.clj +++ b/src/test/clojure/clojure/data/xml/test_seq_tree.clj @@ -9,12 +9,12 @@ (ns ^{:doc "Tests for seq-tree, building a lazy tree from lazy seq." :author "Chris Houser"} clojure.data.xml.test-seq-tree - (:use [clojure.test :only [deftest is are]] - [clojure.data.xml :as xml :only []]) + (:use clojure.test + clojure.data.xml) (:import (java.lang.ref WeakReference))) (def tt - (partial #'xml/seq-tree #(when (= %1 :<) (vector %2)) #{:>} str)) + (partial #'seq-tree #(when (= %1 :<) (vector %2)) #{:>} str)) (deftest example (is (= '(("1" "2" [("3" [("4")])] "5") 6) diff --git a/src/test/clojure/clojure/data/xml/test_sexp.clj b/src/test/clojure/clojure/data/xml/test_sexp.clj index 1109804..6f23a6f 100644 --- a/src/test/clojure/clojure/data/xml/test_sexp.clj +++ b/src/test/clojure/clojure/data/xml/test_sexp.clj @@ -9,10 +9,10 @@ (ns ^{:doc "Tests for reading [:tag {:attr 'value} body*] as XML." :author "Alan Malloy"} clojure.data.xml.test-sexp - (:use [clojure.test :only [deftest is are]] - [clojure.data.xml :as xml :only [sexp-as-element - sexps-as-fragment element]] + (:use clojure.test + clojure.data.xml [clojure.data.xml.test-utils :only (test-stream lazy-parse*)])) + (deftest as-element (let [xml-input "" sexp-input [:tag {:attr "value"} :body]] From 9c199ce1902080b1afac271d8b3ecd2a9f249c3d Mon Sep 17 00:00:00 2001 From: Ryan Senior Date: Tue, 22 May 2012 12:41:45 -0500 Subject: [PATCH 015/237] Fix test failing on Clojure 1.2.0/1.2.1 due to ordering of attributes --- src/test/clojure/clojure/data/xml/test_emit.clj | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/test/clojure/clojure/data/xml/test_emit.clj b/src/test/clojure/clojure/data/xml/test_emit.clj index 2a95641..693e86f 100644 --- a/src/test/clojure/clojure/data/xml/test_emit.clj +++ b/src/test/clojure/clojure/data/xml/test_emit.clj @@ -36,12 +36,14 @@ (is (= expect (emit-str deep-tree))))) (deftest mixed-quotes - (is (= (str "" - "") - (emit-str (element :mixed - {:single "'single'quotes'here" - :double "\"double\"quotes\"here\""}))))) + (is (= (lazy-parse* + (str "" + "")) + (lazy-parse* + (emit-str (element :mixed + {:single "'single'quotes'here" + :double "\"double\"quotes\"here\""})))))) ;; TODO add an indentation test once we figure out how to indent portably across JREs From 7d12b44e12d5a96bc15d575ccda6ec766dad2a5d Mon Sep 17 00:00:00 2001 From: "Hudson @ build.clojure.org" Date: Wed, 23 May 2012 12:15:34 -0500 Subject: [PATCH 016/237] [maven-release-plugin] prepare release data.xml-0.0.4 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9b9bf35..9ef427d 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ 4.0.0 data.xml - 0.0.4-SNAPSHOT + 0.0.4 data.xml jar Functions to parse XML into lazy sequences and lazy trees and emit these as text From 44d894b64d9879233dbb01685bf8370621522b38 Mon Sep 17 00:00:00 2001 From: "Hudson @ build.clojure.org" Date: Wed, 23 May 2012 12:15:34 -0500 Subject: [PATCH 017/237] [maven-release-plugin] prepare for next development iteration --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9ef427d..86708a2 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ 4.0.0 data.xml - 0.0.4 + 0.0.5-SNAPSHOT data.xml jar Functions to parse XML into lazy sequences and lazy trees and emit these as text From 857ffb34e424b79500afe163fb9005d4f94062da Mon Sep 17 00:00:00 2001 From: Ryan Senior Date: Fri, 22 Jun 2012 09:30:56 -0500 Subject: [PATCH 018/237] Added a step in emitting XML that translates the elements to events lazily then emits the events to the stream writer. Fixes emitting documents that have 2+ nested elements with a child that is a large lazy seq followed by a large lazy-seq --- src/main/clojure/clojure/data/xml.clj | 96 ++++++++++++++++++--------- 1 file changed, 66 insertions(+), 30 deletions(-) diff --git a/src/main/clojure/clojure/data/xml.clj b/src/main/clojure/clojure/data/xml.clj index b18e17f..1e39806 100644 --- a/src/main/clojure/clojure/data/xml.clj +++ b/src/main/clojure/clojure/data/xml.clj @@ -24,10 +24,7 @@ (defn event [type name & [attrs str]] (Event. type name attrs str)) -(defprotocol Emit - (emit-element [element writer])) - -(defn write-attributes [{:keys (attrs)} ^javax.xml.stream.XMLStreamWriter writer] +(defn write-attributes [attrs ^javax.xml.stream.XMLStreamWriter writer] (doseq [[k v] attrs] (if (namespace k) (.writeAttribute writer (str (namespace k)) (name k) (str v)) @@ -35,33 +32,71 @@ ; Represents a node of an XML tree (defrecord Element [tag attrs content]) - -(extend-protocol Emit +(defrecord CData [content]) +(defrecord Comment [content]) + +(defn emit-start-tag [event writer] + (let [nspace (namespace (:name event)) + qname (name (:name event))] + (.writeStartElement writer "" qname (or nspace "")) + (write-attributes (:attrs event) writer))) + +(defn emit-event [event writer] + (case (:type event) + :start-element (emit-start-tag event writer) + :end-element (.writeEndElement writer) + :chars (.writeCharacters writer (:str event)) + :cdata (.writeCData writer (:str event)) + :comment (.writeComment writer (:str event)))) + +(defprotocol Flatten + (gen-event [e]) + (next-events [e next])) + +(extend-protocol Flatten Element - (emit-element [e writer] - (let [nspace (namespace (:tag e)) - qname (name (:tag e)) - ^javax.xml.stream.XMLStreamWriter writer writer] - (.writeStartElement writer "" qname (or nspace "")) - (write-attributes e writer) - (doseq [c (:content e)] - (emit-element c writer)) - (.writeEndElement writer)))) - -(defrecord CData [content] - Emit - (emit-element [e writer] - (.writeCData ^javax.xml.stream.XMLStreamWriter writer (:content e)))) - -(defrecord Comment [content] - Emit - (emit-element [e writer] - (.writeComment ^javax.xml.stream.XMLStreamWriter writer (:content e)))) - -(extend-protocol Emit + (gen-event [e] + (Event. :start-element (:tag e) (:attrs e) nil)) + (next-events [e next] + (cons (:content e) + (cons (Event. :end-element (:tag e) nil nil) next))) + Event + (gen-event [e] e) + (next-events [e next] next) + + clojure.lang.Sequential + (gen-event [e] + (gen-event (first e))) + (next-events [e next] + (if-let [r (seq (rest e))] + (lazy-seq (cons (next-events (first e) (rest e)) + next)) + (next-events (first e) next))) String - (emit-element [e writer] - (.writeCharacters ^javax.xml.stream.XMLStreamWriter writer e))) + (gen-event [e] + (Event. :chars nil nil e)) + (next-events [e next] next) + + CData + (gen-event [e] + (Event. :cdata nil nil (:content e))) + (next-events [e next] next) + + Comment + (gen-event [e] + (Event. :comment nil nil (:content e))) + (next-events [e next] next) + + nil + (gen-event [e] (Event. :chars nil nil "")) + (next-events [e next] next)) + +(defn flatten-tree [elements] + (when (seq elements) + (lazy-seq + (let [e (first elements)] + (cons (gen-event e) + (flatten-tree (next-events e (rest elements)))))))) (defn element [tag & [attrs & content]] (Element. tag (or attrs {}) (remove nil? content))) @@ -287,7 +322,8 @@ (check-stream-encoding stream (or (:encoding opts) "UTF-8"))) (.writeStartDocument writer (or (:encoding opts) "UTF-8") "1.0") - (emit-element e writer) + (doseq [event (flatten-tree [e])] + (emit-event event writer)) (.writeEndDocument writer) stream)) From 811cb8434f6f8160093d359aecc5bf4b279a1aea Mon Sep 17 00:00:00 2001 From: Ryan Senior Date: Fri, 22 Jun 2012 09:51:48 -0500 Subject: [PATCH 019/237] Add some type hints to the new emit event code --- src/main/clojure/clojure/data/xml.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/clojure/data/xml.clj b/src/main/clojure/clojure/data/xml.clj index 1e39806..cb00655 100644 --- a/src/main/clojure/clojure/data/xml.clj +++ b/src/main/clojure/clojure/data/xml.clj @@ -35,13 +35,13 @@ (defrecord CData [content]) (defrecord Comment [content]) -(defn emit-start-tag [event writer] +(defn emit-start-tag [event ^javax.xml.stream.XMLStreamWriter writer] (let [nspace (namespace (:name event)) qname (name (:name event))] (.writeStartElement writer "" qname (or nspace "")) (write-attributes (:attrs event) writer))) -(defn emit-event [event writer] +(defn emit-event [event ^javax.xml.stream.XMLStreamWriter writer] (case (:type event) :start-element (emit-start-tag event writer) :end-element (.writeEndElement writer) From 6f52675709dcb781b506c6f689c647baa028a525 Mon Sep 17 00:00:00 2001 From: Ryan Senior Date: Tue, 26 Jun 2012 10:45:57 -0500 Subject: [PATCH 020/237] Removed a unecessary lazy-seq around emit events, speeds up emitting --- src/main/clojure/clojure/data/xml.clj | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/clojure/clojure/data/xml.clj b/src/main/clojure/clojure/data/xml.clj index cb00655..a7de93a 100644 --- a/src/main/clojure/clojure/data/xml.clj +++ b/src/main/clojure/clojure/data/xml.clj @@ -69,8 +69,7 @@ (gen-event (first e))) (next-events [e next] (if-let [r (seq (rest e))] - (lazy-seq (cons (next-events (first e) (rest e)) - next)) + (cons (next-events (first e) r) next) (next-events (first e) next))) String (gen-event [e] From bc00307b0da969f401fe821ed759134267ff186b Mon Sep 17 00:00:00 2001 From: Ryan Senior Date: Tue, 26 Jun 2012 11:25:13 -0500 Subject: [PATCH 021/237] Some minor renaming in the new event generation code for emitting elements --- src/main/clojure/clojure/data/xml.clj | 75 +++++++++++++++------------ 1 file changed, 43 insertions(+), 32 deletions(-) diff --git a/src/main/clojure/clojure/data/xml.clj b/src/main/clojure/clojure/data/xml.clj index a7de93a..2e5c783 100644 --- a/src/main/clojure/clojure/data/xml.clj +++ b/src/main/clojure/clojure/data/xml.clj @@ -49,53 +49,64 @@ :cdata (.writeCData writer (:str event)) :comment (.writeComment writer (:str event)))) -(defprotocol Flatten - (gen-event [e]) - (next-events [e next])) - -(extend-protocol Flatten +(defprotocol EventGeneration + "Protocol for generating new events based on element type" + (gen-event [item] + "Function to generate an event for e.") + (next-events [item next-items] + "Returns the next set of events that should occur after e. next-events are the + events that should be generated after this one is complete.")) + +(extend-protocol EventGeneration Element - (gen-event [e] - (Event. :start-element (:tag e) (:attrs e) nil)) - (next-events [e next] - (cons (:content e) - (cons (Event. :end-element (:tag e) nil nil) next))) + (gen-event [element] + (Event. :start-element (:tag element) (:attrs element) nil)) + (next-events [element next-items] + (cons (:content element) + (cons (Event. :end-element (:tag element) nil nil) next-items))) Event - (gen-event [e] e) - (next-events [e next] next) + (gen-event [event] event) + (next-events [_ next-items] + next-items) clojure.lang.Sequential - (gen-event [e] - (gen-event (first e))) - (next-events [e next] - (if-let [r (seq (rest e))] - (cons (next-events (first e) r) next) - (next-events (first e) next))) + (gen-event [coll] + (gen-event (first coll))) + (next-events [coll next-items] + (if-let [r (seq (rest coll))] + (cons (next-events (first coll) r) next-items) + (next-events (first coll) next-items))) + String - (gen-event [e] - (Event. :chars nil nil e)) - (next-events [e next] next) + (gen-event [s] + (Event. :chars nil nil s)) + (next-events [_ next-items] + next-items) CData - (gen-event [e] - (Event. :cdata nil nil (:content e))) - (next-events [e next] next) + (gen-event [cdata] + (Event. :cdata nil nil (:content cdata))) + (next-events [_ next-items] + next-items) Comment - (gen-event [e] - (Event. :comment nil nil (:content e))) - (next-events [e next] next) + (gen-event [comment] + (Event. :comment nil nil (:content comment))) + (next-events [_ next-items] + next-items) nil - (gen-event [e] (Event. :chars nil nil "")) - (next-events [e next] next)) + (gen-event [_] + (Event. :chars nil nil "")) + (next-events [_ next-items] + next-items)) -(defn flatten-tree [elements] +(defn flatten-elements [elements] (when (seq elements) (lazy-seq (let [e (first elements)] (cons (gen-event e) - (flatten-tree (next-events e (rest elements)))))))) + (flatten-elements (next-events e (rest elements)))))))) (defn element [tag & [attrs & content]] (Element. tag (or attrs {}) (remove nil? content))) @@ -321,7 +332,7 @@ (check-stream-encoding stream (or (:encoding opts) "UTF-8"))) (.writeStartDocument writer (or (:encoding opts) "UTF-8") "1.0") - (doseq [event (flatten-tree [e])] + (doseq [event (flatten-elements [e])] (emit-event event writer)) (.writeEndDocument writer) stream)) From 680dc2ba7a5dab8d95eae42f6e9fd8df49902855 Mon Sep 17 00:00:00 2001 From: Ryan Senior Date: Tue, 26 Jun 2012 11:31:18 -0500 Subject: [PATCH 022/237] Removed the sexp-as-fragment code, it's no longer used --- src/main/clojure/clojure/data/xml.clj | 63 +--------- .../clojure/clojure/data/xml/test_emit.clj | 115 ++++++++++++++++++ .../clojure/clojure/data/xml/test_sexp.clj | 27 ---- 3 files changed, 117 insertions(+), 88 deletions(-) delete mode 100644 src/test/clojure/clojure/data/xml/test_sexp.clj diff --git a/src/main/clojure/clojure/data/xml.clj b/src/main/clojure/clojure/data/xml.clj index 2e5c783..5d76161 100644 --- a/src/main/clojure/clojure/data/xml.clj +++ b/src/main/clojure/clojure/data/xml.clj @@ -65,7 +65,8 @@ (cons (:content element) (cons (Event. :end-element (:tag element) nil nil) next-items))) Event - (gen-event [event] event) + (gen-event [event] + event) (next-events [_ next-items] next-items) @@ -167,66 +168,6 @@ (fn [^Event event] (.str event)) events))) -(defprotocol AsElements - (as-elements [expr] "Return a seq of elements represented by an expression.")) - -(extend-protocol AsElements - clojure.lang.IPersistentVector - (as-elements [v] - (let [[tag & [attrs & after-attrs :as content]] v - [attrs content] (if (map? attrs) - [(into {} (for [[k v] attrs] - [k (str v)])) - after-attrs] - [{} content])] - [(Element. tag attrs (mapcat as-elements content))])) - - clojure.lang.ISeq - (as-elements [s] - (mapcat as-elements s)) - - clojure.lang.Keyword - (as-elements [k] - [(Element. k {} ())]) - - java.lang.String - (as-elements [s] - [s]) - - nil - (as-elements [_] nil) - - java.lang.Object - (as-elements [o] - [(str o)])) - -(defn sexps-as-fragment - "Convert a compact prxml/hiccup-style data structure into the more formal - tag/attrs/content format. A seq of elements will be returned, which may - not be suitable for immediate use as there is no root element. See also - sexp-as-element. - - The format is [:tag-name attr-map? content*]. Each vector opens a new tag; - seqs do not open new tags, and are just used for inserting groups of elements - into the parent tag. A bare keyword not in a vector creates an empty element. - - To provide XML conversion for your own data types, extend the AsElements - protocol to them." - ([] nil) - ([sexp] (as-elements sexp)) - ([sexp & sexps] (mapcat as-elements (cons sexp sexps)))) - -(defn sexp-as-element - "Convert a single sexp into an Element" - [sexp] - (let [[root & more] (sexps-as-fragment sexp)] - (when more - (throw - (IllegalArgumentException. - "Cannot have multiple root elements; try creating a fragment instead"))) - root)) - - (defn- attr-prefix [^XMLStreamReader sreader index] (let [p (.getAttributePrefix sreader index)] (when-not (str/blank? p) diff --git a/src/test/clojure/clojure/data/xml/test_emit.clj b/src/test/clojure/clojure/data/xml/test_emit.clj index 693e86f..1533bcf 100644 --- a/src/test/clojure/clojure/data/xml/test_emit.clj +++ b/src/test/clojure/clojure/data/xml/test_emit.clj @@ -98,3 +98,118 @@ expect (str "\n\n " "\n \n foo\n \n \n\n")] (is (= expect (indent-str nested-xml))))) + +(comment + + + + +(import '[clojure.data.xml Element]) + + ;; + ;; Doesn't work + ;; + (defn just-elements2 [total] + (Element. :sparql {:xmlns "http://www.w3.org/2005/sparql-results#"} + (list + (Element. :foo {} + (for [x (range 1 total)] + (Element. :result {} + (list + (Element. :binding {:name "a"} (list (Element. :literal {} (list (str x))))) + (Element. :binding {:name "b"} (list (Element. :literal {} (list (str "b" x))))) + (Element. :binding {:name "c"} (list (Element. :literal {} (list (str "c" x)))))))))))) + + + + + + + + + ;; + ;; Works + ;; + (defn just-elements [total] + (Element. :sparql {:xmlns "http://www.w3.org/2005/sparql-results#"} + (for [x (range 1 total)] + (Element. :result {} + (list + (Element. :binding {:name "a"} (list (Element. :literal {} (list (str x))))) + (Element. :binding {:name "b"} (list (Element. :literal {} (list (str "b" x))))) + (Element. :binding {:name "c"} (list (Element. :literal {} (list (str "c" x)))))))))) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + (defn just-elements4 [total] + (Element. :sparql {:xmlns "http://www.w3.org/2005/sparql-results#"} + (list + (Element. :foo {} + (for [x (range 1 total)] + (Element. :result {} + (list + (Element. :binding {:name "a"} (list )) + (Element. :binding {:name "b"} (list )) + (Element. :binding {:name "c"} (list )))))))) + + ) + + (defn just-elements-nate [total] + (Element. :sparql {:xmlns "http://www.w3.org/2005/sparql-results#"} + (lazy-seq + (list + (Element. :foo {} + (for [x (range 1 total)] + (Element. :result {} + (list + (Element. :binding {:name "a"} (list (Element. :literal {} (list (str x))))) + (Element. :binding {:name "b"} (list (Element. :literal {} (list (str "b" x))))) + (Element. :binding {:name "c"} (list (Element. :literal {} (list (str "c" x))))))))))))) + + (defn just-elements3 [total] + [:sparql {:xmlns "http://www.w3.org/2005/sparql-results#"} + [[:foo {} + (for [x (range 1 total)] + [:result {} + [[:binding {:name "a"} [[:literal {} (str x)]]] + [:binding {:name "b"} [[:literal {} (str "b" x)]]] + [:binding {:name "c"} [[:literal {} (str "c" x)]]]]])]]]) + + + (defn output-big-xml [path doc] + (with-open [oo (java.io.FileWriter. path)] + (emit doc oo))) + + (defn output-big-xml-event [path doc] + (with-open [oo (java.io.FileWriter. path)] + (emit-events doc oo))) + + #_(defn output-big-xml2 [path doc] + (with-open [oo (java.io.FileWriter. path)] + (emit2 doc oo)))) \ No newline at end of file diff --git a/src/test/clojure/clojure/data/xml/test_sexp.clj b/src/test/clojure/clojure/data/xml/test_sexp.clj deleted file mode 100644 index 6f23a6f..0000000 --- a/src/test/clojure/clojure/data/xml/test_sexp.clj +++ /dev/null @@ -1,27 +0,0 @@ -; Copyright (c) Rich Hickey. 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 ^{:doc "Tests for reading [:tag {:attr 'value} body*] as XML." - :author "Alan Malloy"} - clojure.data.xml.test-sexp - (:use clojure.test - clojure.data.xml - [clojure.data.xml.test-utils :only (test-stream lazy-parse*)])) - -(deftest as-element - (let [xml-input "" - sexp-input [:tag {:attr "value"} :body]] - (is (= (lazy-parse* xml-input) - (sexp-as-element sexp-input))))) - -(deftest as-fragment - (let [input (list [:tag1 "stuff"] - [:tag2 "other"])] - (is (= (sexps-as-fragment input) - (map sexp-as-element input))) - (is (thrown? Exception (sexp-as-element input))))) From 0afe3a2559ab3222310be39810010cc69951a027 Mon Sep 17 00:00:00 2001 From: "Hudson @ build.clojure.org" Date: Tue, 26 Jun 2012 12:35:56 -0500 Subject: [PATCH 023/237] [maven-release-plugin] prepare release data.xml-0.0.5 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 86708a2..5a7fbb6 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ 4.0.0 data.xml - 0.0.5-SNAPSHOT + 0.0.5 data.xml jar Functions to parse XML into lazy sequences and lazy trees and emit these as text From e407e7f165aca9893aaf1e78e45ebd82b4eb0a4a Mon Sep 17 00:00:00 2001 From: "Hudson @ build.clojure.org" Date: Tue, 26 Jun 2012 12:35:56 -0500 Subject: [PATCH 024/237] [maven-release-plugin] prepare for next development iteration --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5a7fbb6..8dedfaf 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ 4.0.0 data.xml - 0.0.5 + 0.0.6-SNAPSHOT data.xml jar Functions to parse XML into lazy sequences and lazy trees and emit these as text From 205a9df7ea77679b4d4a253a68775859fb13a1a1 Mon Sep 17 00:00:00 2001 From: Ryan Senior Date: Thu, 28 Jun 2012 21:29:38 -0500 Subject: [PATCH 025/237] Revert "Removed the sexp-as-fragment code, it's no longer used" This reverts commit 680dc2ba7a5dab8d95eae42f6e9fd8df49902855. --- src/main/clojure/clojure/data/xml.clj | 63 +++++++++- .../clojure/clojure/data/xml/test_emit.clj | 115 ------------------ .../clojure/clojure/data/xml/test_sexp.clj | 27 ++++ 3 files changed, 88 insertions(+), 117 deletions(-) create mode 100644 src/test/clojure/clojure/data/xml/test_sexp.clj diff --git a/src/main/clojure/clojure/data/xml.clj b/src/main/clojure/clojure/data/xml.clj index 5d76161..2e5c783 100644 --- a/src/main/clojure/clojure/data/xml.clj +++ b/src/main/clojure/clojure/data/xml.clj @@ -65,8 +65,7 @@ (cons (:content element) (cons (Event. :end-element (:tag element) nil nil) next-items))) Event - (gen-event [event] - event) + (gen-event [event] event) (next-events [_ next-items] next-items) @@ -168,6 +167,66 @@ (fn [^Event event] (.str event)) events))) +(defprotocol AsElements + (as-elements [expr] "Return a seq of elements represented by an expression.")) + +(extend-protocol AsElements + clojure.lang.IPersistentVector + (as-elements [v] + (let [[tag & [attrs & after-attrs :as content]] v + [attrs content] (if (map? attrs) + [(into {} (for [[k v] attrs] + [k (str v)])) + after-attrs] + [{} content])] + [(Element. tag attrs (mapcat as-elements content))])) + + clojure.lang.ISeq + (as-elements [s] + (mapcat as-elements s)) + + clojure.lang.Keyword + (as-elements [k] + [(Element. k {} ())]) + + java.lang.String + (as-elements [s] + [s]) + + nil + (as-elements [_] nil) + + java.lang.Object + (as-elements [o] + [(str o)])) + +(defn sexps-as-fragment + "Convert a compact prxml/hiccup-style data structure into the more formal + tag/attrs/content format. A seq of elements will be returned, which may + not be suitable for immediate use as there is no root element. See also + sexp-as-element. + + The format is [:tag-name attr-map? content*]. Each vector opens a new tag; + seqs do not open new tags, and are just used for inserting groups of elements + into the parent tag. A bare keyword not in a vector creates an empty element. + + To provide XML conversion for your own data types, extend the AsElements + protocol to them." + ([] nil) + ([sexp] (as-elements sexp)) + ([sexp & sexps] (mapcat as-elements (cons sexp sexps)))) + +(defn sexp-as-element + "Convert a single sexp into an Element" + [sexp] + (let [[root & more] (sexps-as-fragment sexp)] + (when more + (throw + (IllegalArgumentException. + "Cannot have multiple root elements; try creating a fragment instead"))) + root)) + + (defn- attr-prefix [^XMLStreamReader sreader index] (let [p (.getAttributePrefix sreader index)] (when-not (str/blank? p) diff --git a/src/test/clojure/clojure/data/xml/test_emit.clj b/src/test/clojure/clojure/data/xml/test_emit.clj index 1533bcf..693e86f 100644 --- a/src/test/clojure/clojure/data/xml/test_emit.clj +++ b/src/test/clojure/clojure/data/xml/test_emit.clj @@ -98,118 +98,3 @@ expect (str "\n\n " "\n \n foo\n \n \n\n")] (is (= expect (indent-str nested-xml))))) - -(comment - - - - -(import '[clojure.data.xml Element]) - - ;; - ;; Doesn't work - ;; - (defn just-elements2 [total] - (Element. :sparql {:xmlns "http://www.w3.org/2005/sparql-results#"} - (list - (Element. :foo {} - (for [x (range 1 total)] - (Element. :result {} - (list - (Element. :binding {:name "a"} (list (Element. :literal {} (list (str x))))) - (Element. :binding {:name "b"} (list (Element. :literal {} (list (str "b" x))))) - (Element. :binding {:name "c"} (list (Element. :literal {} (list (str "c" x)))))))))))) - - - - - - - - - ;; - ;; Works - ;; - (defn just-elements [total] - (Element. :sparql {:xmlns "http://www.w3.org/2005/sparql-results#"} - (for [x (range 1 total)] - (Element. :result {} - (list - (Element. :binding {:name "a"} (list (Element. :literal {} (list (str x))))) - (Element. :binding {:name "b"} (list (Element. :literal {} (list (str "b" x))))) - (Element. :binding {:name "c"} (list (Element. :literal {} (list (str "c" x)))))))))) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - (defn just-elements4 [total] - (Element. :sparql {:xmlns "http://www.w3.org/2005/sparql-results#"} - (list - (Element. :foo {} - (for [x (range 1 total)] - (Element. :result {} - (list - (Element. :binding {:name "a"} (list )) - (Element. :binding {:name "b"} (list )) - (Element. :binding {:name "c"} (list )))))))) - - ) - - (defn just-elements-nate [total] - (Element. :sparql {:xmlns "http://www.w3.org/2005/sparql-results#"} - (lazy-seq - (list - (Element. :foo {} - (for [x (range 1 total)] - (Element. :result {} - (list - (Element. :binding {:name "a"} (list (Element. :literal {} (list (str x))))) - (Element. :binding {:name "b"} (list (Element. :literal {} (list (str "b" x))))) - (Element. :binding {:name "c"} (list (Element. :literal {} (list (str "c" x))))))))))))) - - (defn just-elements3 [total] - [:sparql {:xmlns "http://www.w3.org/2005/sparql-results#"} - [[:foo {} - (for [x (range 1 total)] - [:result {} - [[:binding {:name "a"} [[:literal {} (str x)]]] - [:binding {:name "b"} [[:literal {} (str "b" x)]]] - [:binding {:name "c"} [[:literal {} (str "c" x)]]]]])]]]) - - - (defn output-big-xml [path doc] - (with-open [oo (java.io.FileWriter. path)] - (emit doc oo))) - - (defn output-big-xml-event [path doc] - (with-open [oo (java.io.FileWriter. path)] - (emit-events doc oo))) - - #_(defn output-big-xml2 [path doc] - (with-open [oo (java.io.FileWriter. path)] - (emit2 doc oo)))) \ No newline at end of file diff --git a/src/test/clojure/clojure/data/xml/test_sexp.clj b/src/test/clojure/clojure/data/xml/test_sexp.clj new file mode 100644 index 0000000..6f23a6f --- /dev/null +++ b/src/test/clojure/clojure/data/xml/test_sexp.clj @@ -0,0 +1,27 @@ +; Copyright (c) Rich Hickey. 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 ^{:doc "Tests for reading [:tag {:attr 'value} body*] as XML." + :author "Alan Malloy"} + clojure.data.xml.test-sexp + (:use clojure.test + clojure.data.xml + [clojure.data.xml.test-utils :only (test-stream lazy-parse*)])) + +(deftest as-element + (let [xml-input "" + sexp-input [:tag {:attr "value"} :body]] + (is (= (lazy-parse* xml-input) + (sexp-as-element sexp-input))))) + +(deftest as-fragment + (let [input (list [:tag1 "stuff"] + [:tag2 "other"])] + (is (= (sexps-as-fragment input) + (map sexp-as-element input))) + (is (thrown? Exception (sexp-as-element input))))) From b407d717b67b9eb6f9ea7106ab23b7bb37ed2acb Mon Sep 17 00:00:00 2001 From: Ryan Senior Date: Fri, 29 Jun 2012 06:23:01 -0500 Subject: [PATCH 026/237] Updated data.xml version in README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9981f03..3e0c261 100644 --- a/README.md +++ b/README.md @@ -36,13 +36,13 @@ For Maven projects, add the following XML in your `pom.xml`'s `` s org.clojure data.xml - 0.0.3 + 0.0.5 ### Leiningen Add the following to the `project.clj` dependencies: - [org.clojure/data.xml "0.0.3"] + [org.clojure/data.xml "0.0.5"] ## Examples From 715e5069216896f427ff6ad86ad092641b52fe3c Mon Sep 17 00:00:00 2001 From: Ryan Senior Date: Fri, 29 Jun 2012 16:31:30 -0500 Subject: [PATCH 027/237] Add an example for sexp-as-element --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 3e0c261..d9cc64d 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,17 @@ functions or the element function used below, and written using a ;;-> Writes XML to /tmp/foo.xml +The same can also be expressed using a more Hiccup-like style of defining the elements using sexp-as-element: + + (= (element :foo {:foo-attr "foo value"} + (element :bar {:bar-attr "bar value"} + (element :baz {} "The baz value"))) + (sexp-as-element + [:foo {:foo-attr "foo value"} + [:bar {:bar-attr "bar value"} + [:baz {} "The baz value"]]])) + ;;-> true + XML can be "round tripped" through the library: (let [tags (element :foo {:foo-attr "foo value"} From fdbb63b8ef25b9cf6106e62fc31df8b4219bb30c Mon Sep 17 00:00:00 2001 From: "Hudson @ build.clojure.org" Date: Fri, 29 Jun 2012 16:41:00 -0500 Subject: [PATCH 028/237] [maven-release-plugin] prepare release data.xml-0.0.6 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8dedfaf..eb07083 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ 4.0.0 data.xml - 0.0.6-SNAPSHOT + 0.0.6 data.xml jar Functions to parse XML into lazy sequences and lazy trees and emit these as text From df3034b23e990e72226e7218d5dcc0c43de1dff1 Mon Sep 17 00:00:00 2001 From: "Hudson @ build.clojure.org" Date: Fri, 29 Jun 2012 16:41:00 -0500 Subject: [PATCH 029/237] [maven-release-plugin] prepare for next development iteration --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index eb07083..3ad83e7 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ 4.0.0 data.xml - 0.0.6 + 0.0.7-SNAPSHOT data.xml jar Functions to parse XML into lazy sequences and lazy trees and emit these as text From 0543100082856adc5d5db0683b2c397d6256bd5e Mon Sep 17 00:00:00 2001 From: Ryan Senior Date: Fri, 29 Jun 2012 16:42:16 -0500 Subject: [PATCH 030/237] Bumped the README to 0.0.6 --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d9cc64d..43fe852 100644 --- a/README.md +++ b/README.md @@ -36,13 +36,13 @@ For Maven projects, add the following XML in your `pom.xml`'s `` s org.clojure data.xml - 0.0.5 + 0.0.6 ### Leiningen Add the following to the `project.clj` dependencies: - [org.clojure/data.xml "0.0.5"] + [org.clojure/data.xml "0.0.6"] ## Examples From 2c1c598218b8c2bdf477e20663e59290d6686ca0 Mon Sep 17 00:00:00 2001 From: Ryan Senior Date: Wed, 8 Aug 2012 13:24:47 -0500 Subject: [PATCH 031/237] Updated README to contrib standard --- README.md | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 43fe852..f29083d 100644 --- a/README.md +++ b/README.md @@ -21,14 +21,13 @@ information on this is available [here](https://github.com/clojure/data.xml/blob Please report bugs using JIRA [here](http://dev.clojure.org/jira/browse/DXML). -## Contributing +## Installation -All contributions need to be made via patches attached to tickets in -[JIRA](http://dev.clojure.org/jira/browse/DXML). Check the -[Contributing to Clojure](http://clojure.org/contributing) page for -more information. +Latest stable release: 0.0.6 -## Installation +* [All Released Versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22data.xml%22) + +* [Development Snapshot Versions](https://oss.sonatype.org/index.html#nexus-search;gav~org.clojure~data.xml~~~) ### Maven For Maven projects, add the following XML in your `pom.xml`'s `` section: @@ -170,6 +169,27 @@ But are ignored when read: "and another element" +Generated API docs for data.xml are available [here](http://clojure.github.com/data.xml). + ## License Licensed under the [Eclipse Public License](http://www.opensource.org/licenses/eclipse-1.0.php). + +## Developer Information + +* [GitHub project](https://github.com/clojure/data.xml) + +* [Bug Tracker](http://dev.clojure.org/jira/browse/DXML) + +* [Continuous Integration](http://build.clojure.org/job/data.xml/) + +* [Compatibility Test Matrix](http://build.clojure.org/job/data.xml-test-matrix/) + +## Contributing + +All contributions need to be made via patches attached to tickets in +[JIRA](http://dev.clojure.org/jira/browse/DXML). Check the +[Contributing to Clojure](http://clojure.org/contributing) page for +more information. + + From 2e087ceec22e788356a0513a2f216dff97a49706 Mon Sep 17 00:00:00 2001 From: Ryan Senior Date: Tue, 9 Oct 2012 22:39:39 -0500 Subject: [PATCH 032/237] Fixed indent with optional args DXML-7 --- src/main/clojure/clojure/data/xml.clj | 2 +- src/test/clojure/clojure/data/xml/test_emit.clj | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/clojure/data/xml.clj b/src/main/clojure/clojure/data/xml.clj index 2e5c783..d53259e 100644 --- a/src/main/clojure/clojure/data/xml.clj +++ b/src/main/clojure/clojure/data/xml.clj @@ -356,7 +356,7 @@ debugging/testing only." [e ^java.io.Writer stream & {:as opts}] (let [sw (java.io.StringWriter.) - _ (apply emit e sw opts) + _ (apply emit e sw (apply concat opts)) source (-> sw .toString java.io.StringReader. javax.xml.transform.stream.StreamSource.) result (javax.xml.transform.stream.StreamResult. stream)] (.transform (indenting-transformer) source result))) diff --git a/src/test/clojure/clojure/data/xml/test_emit.clj b/src/test/clojure/clojure/data/xml/test_emit.clj index 693e86f..0d43099 100644 --- a/src/test/clojure/clojure/data/xml/test_emit.clj +++ b/src/test/clojure/clojure/data/xml/test_emit.clj @@ -98,3 +98,11 @@ expect (str "\n\n " "\n \n foo\n \n \n\n")] (is (= expect (indent-str nested-xml))))) + +(deftest test-indent + (let [nested-xml (lazy-parse* (str "foo")) + expect (str "\n\n " + "\n \n foo\n \n \n\n") + sw (java.io.StringWriter.)] + (indent nested-xml sw :encoding "UTF-8") + (is (= expect (.toString sw))))) \ No newline at end of file From bc0386515476f6878602133e25d4abaa68a51a0b Mon Sep 17 00:00:00 2001 From: Andy Fingerhut Date: Sun, 28 Oct 2012 16:09:12 -0700 Subject: [PATCH 033/237] DXML-9: Eliminate one instance of reflection via a type hint Signed-off-by: Ryan Senior --- src/main/clojure/clojure/data/xml.clj | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/clojure/data/xml.clj b/src/main/clojure/clojure/data/xml.clj index d53259e..0bf5ecc 100644 --- a/src/main/clojure/clojure/data/xml.clj +++ b/src/main/clojure/clojure/data/xml.clj @@ -291,6 +291,10 @@ and xml-input-factory-props for more information. Defaults coalescing true." [s & {:as props}] (let [fac (new-xml-input-factory (merge {:coalescing true} props)) + ;; Reflection on following line cannot be eliminated via a + ;; type hint, because s is advertised by fn parse to be an + ;; InputStream or Reader, and there are different + ;; createXMLStreamReader signatures for each of those types. sreader (.createXMLStreamReader fac s)] (pull-seq sreader))) @@ -344,7 +348,7 @@ (emit e sw) (.toString sw))) -(defn indenting-transformer [] +(defn ^javax.xml.transform.Transformer indenting-transformer [] (doto (-> (javax.xml.transform.TransformerFactory/newInstance) .newTransformer) (.setOutputProperty (javax.xml.transform.OutputKeys/INDENT) "yes") (.setOutputProperty (javax.xml.transform.OutputKeys/METHOD) "xml") From 620b21f023d09d2150253be8a17977d2a406525c Mon Sep 17 00:00:00 2001 From: Ryan Senior Date: Tue, 13 Nov 2012 00:34:04 -0600 Subject: [PATCH 034/237] Add support for Strings as tag and attribute names (DXML-8) --- src/main/clojure/clojure/data/xml.clj | 18 +++++++++++++----- .../clojure/clojure/data/xml/test_emit.clj | 6 ++++++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/main/clojure/clojure/data/xml.clj b/src/main/clojure/clojure/data/xml.clj index 0bf5ecc..a2a1472 100644 --- a/src/main/clojure/clojure/data/xml.clj +++ b/src/main/clojure/clojure/data/xml.clj @@ -24,11 +24,20 @@ (defn event [type name & [attrs str]] (Event. type name attrs str)) +(defn qualified-name [event-name] + (if (instance? clojure.lang.Named event-name) + [(namespace event-name) (name event-name)] + (let [name-parts (str/split event-name #"/" 2)] + (if (= 2 (count name-parts)) + name-parts + [nil (first name-parts)])))) + (defn write-attributes [attrs ^javax.xml.stream.XMLStreamWriter writer] (doseq [[k v] attrs] - (if (namespace k) - (.writeAttribute writer (str (namespace k)) (name k) (str v)) - (.writeAttribute writer (name k) (str v))))) + (let [[attr-ns attr-name] (qualified-name k)] + (if attr-ns + (.writeAttribute writer attr-ns attr-name (str v)) + (.writeAttribute writer attr-name (str v)))))) ; Represents a node of an XML tree (defrecord Element [tag attrs content]) @@ -36,8 +45,7 @@ (defrecord Comment [content]) (defn emit-start-tag [event ^javax.xml.stream.XMLStreamWriter writer] - (let [nspace (namespace (:name event)) - qname (name (:name event))] + (let [[nspace qname] (qualified-name (:name event))] (.writeStartElement writer "" qname (or nspace "")) (write-attributes (:attrs event) writer))) diff --git a/src/test/clojure/clojure/data/xml/test_emit.clj b/src/test/clojure/clojure/data/xml/test_emit.clj index 0d43099..12cfe96 100644 --- a/src/test/clojure/clojure/data/xml/test_emit.clj +++ b/src/test/clojure/clojure/data/xml/test_emit.clj @@ -35,6 +35,12 @@ "")] (is (= expect (emit-str deep-tree))))) +(deftest defaults + ;;XML below should be updated when namespace support is in + (let [expect (str "done")] + (is (= expect (emit-str (element "foo/bar" {"foo/item" 1} [(element "foo/baz" {"foo/item" 2} "done")])))))) + + (deftest mixed-quotes (is (= (lazy-parse* (str "" From 63c610b1857b837b63acc514665b8c7e3f0b20cd Mon Sep 17 00:00:00 2001 From: Ryan Senior Date: Tue, 11 Dec 2012 21:12:52 -0600 Subject: [PATCH 035/237] added cdata support for sexp-as-element (DXML-11) --- src/main/clojure/clojure/data/xml.clj | 7 ++++++- .../clojure/clojure/data/xml/test_sexp.clj | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/clojure/data/xml.clj b/src/main/clojure/clojure/data/xml.clj index a2a1472..6568f34 100644 --- a/src/main/clojure/clojure/data/xml.clj +++ b/src/main/clojure/clojure/data/xml.clj @@ -178,6 +178,11 @@ (defprotocol AsElements (as-elements [expr] "Return a seq of elements represented by an expression.")) +(defn sexp-element [tag attrs child] + (cond + (= :-cdata tag) (CData. (first child)) + :else (Element. tag attrs (mapcat as-elements child)))) + (extend-protocol AsElements clojure.lang.IPersistentVector (as-elements [v] @@ -187,7 +192,7 @@ [k (str v)])) after-attrs] [{} content])] - [(Element. tag attrs (mapcat as-elements content))])) + [(sexp-element tag attrs content)])) clojure.lang.ISeq (as-elements [s] diff --git a/src/test/clojure/clojure/data/xml/test_sexp.clj b/src/test/clojure/clojure/data/xml/test_sexp.clj index 6f23a6f..3bfa848 100644 --- a/src/test/clojure/clojure/data/xml/test_sexp.clj +++ b/src/test/clojure/clojure/data/xml/test_sexp.clj @@ -25,3 +25,21 @@ (is (= (sexps-as-fragment input) (map sexp-as-element input))) (is (thrown? Exception (sexp-as-element input))))) + +(deftest with-cdata + (let [xml-input (element :tag {:attr "value"} + (element :body {} (cdata "not parsed Date: Tue, 11 Dec 2012 21:21:26 -0600 Subject: [PATCH 036/237] Added comment support for sexp-as-element --- src/main/clojure/clojure/data/xml.clj | 1 + src/test/clojure/clojure/data/xml/test_sexp.clj | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/src/main/clojure/clojure/data/xml.clj b/src/main/clojure/clojure/data/xml.clj index 6568f34..13e120f 100644 --- a/src/main/clojure/clojure/data/xml.clj +++ b/src/main/clojure/clojure/data/xml.clj @@ -181,6 +181,7 @@ (defn sexp-element [tag attrs child] (cond (= :-cdata tag) (CData. (first child)) + (= :-comment tag) (Comment. (first child)) :else (Element. tag attrs (mapcat as-elements child)))) (extend-protocol AsElements diff --git a/src/test/clojure/clojure/data/xml/test_sexp.clj b/src/test/clojure/clojure/data/xml/test_sexp.clj index 3bfa848..04ef20f 100644 --- a/src/test/clojure/clojure/data/xml/test_sexp.clj +++ b/src/test/clojure/clojure/data/xml/test_sexp.clj @@ -43,3 +43,10 @@ [:-cdata "more not parsed Date: Mon, 31 Dec 2012 09:58:30 -0600 Subject: [PATCH 037/237] Updated readme to include S-expression comment/cdata example --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index f29083d..c045776 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,13 @@ The same can also be expressed using a more Hiccup-like style of defining the el [:baz {} "The baz value"]]])) ;;-> true +Comments and CData can also be emitted as an S-expression with the special tag names :-cdata and :-comment: + + (= (element :tag {:attr "value"} + (element :body {} (cdata "not parsed true + XML can be "round tripped" through the library: (let [tags (element :foo {:foo-attr "foo value"} From df7324d80e2eb83063e8680a1d2cc313bf5fe718 Mon Sep 17 00:00:00 2001 From: Ryan Senior Date: Mon, 31 Dec 2012 10:08:11 -0600 Subject: [PATCH 038/237] Update README.md Fixed capitalization of CDATA in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c045776..0f0f1f9 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,7 @@ The same can also be expressed using a more Hiccup-like style of defining the el [:baz {} "The baz value"]]])) ;;-> true -Comments and CData can also be emitted as an S-expression with the special tag names :-cdata and :-comment: +Comments and CDATA can also be emitted as an S-expression with the special tag names :-cdata and :-comment: (= (element :tag {:attr "value"} (element :body {} (cdata "not parsed Date: Tue, 8 Jan 2013 21:22:47 -0600 Subject: [PATCH 039/237] Added proper handling of ]]> occurring in a CDATA section (DXML-12) --- src/main/clojure/clojure/data/xml.clj | 15 ++++++++++++- .../clojure/clojure/data/xml/test_emit.clj | 21 +++++++++++++++++-- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/clojure/data/xml.clj b/src/main/clojure/clojure/data/xml.clj index 13e120f..bf9c9cf 100644 --- a/src/main/clojure/clojure/data/xml.clj +++ b/src/main/clojure/clojure/data/xml.clj @@ -49,12 +49,25 @@ (.writeStartElement writer "" qname (or nspace "")) (write-attributes (:attrs event) writer))) +(defn str-empty? [s] + (or (nil? s) + (= s ""))) + +(defn emit-cdata [^String cdata-str writer] + (when-not (str-empty? cdata-str) + (let [idx (.indexOf cdata-str "]]>")] + (if (= idx -1) + (.writeCData writer cdata-str ) + (do + (.writeCData writer (subs cdata-str 0 idx)) + (recur (subs cdata-str (+ idx 3)) writer)))))) + (defn emit-event [event ^javax.xml.stream.XMLStreamWriter writer] (case (:type event) :start-element (emit-start-tag event writer) :end-element (.writeEndElement writer) :chars (.writeCharacters writer (:str event)) - :cdata (.writeCData writer (:str event)) + :cdata (emit-cdata (:str event) writer) :comment (.writeComment writer (:str event)))) (defprotocol EventGeneration diff --git a/src/test/clojure/clojure/data/xml/test_emit.clj b/src/test/clojure/clojure/data/xml/test_emit.clj index 12cfe96..eb29269 100644 --- a/src/test/clojure/clojure/data/xml/test_emit.clj +++ b/src/test/clojure/clojure/data/xml/test_emit.clj @@ -78,10 +78,27 @@ (emit (element :foo) *out* :encoding "ISO-8859-1")))))) (deftest emitting-cdata + (testing "basic cdata" + (is (= (str "" + "]]>") + (emit-str (element :cdata-stuff {} + (cdata "")))))) + (testing "cdata with ]]> chars" + (is (= (str "" + "]]>]]>") + (emit-str (element :cdata-stuff {} + (cdata "]]>")))))) + (testing "cdata with ]]> chars and newlines" + (is (= (str "" + "\n\n\n]]>]]>") + (emit-str (element :cdata-stuff {} + (cdata "\n\n\n]]>"))))))) + +(deftest emitting-cdata-with-embedded-end (is (= (str "" - "]]>") + "]]>]]>") (emit-str (element :cdata-stuff {} - (cdata ""))))) ) + (cdata "]]>"))))) ) (deftest emitting-comment (is (= (str "" From f5f0ae17226c96d7ea63384938fca0883e01c792 Mon Sep 17 00:00:00 2001 From: Ryan Senior Date: Tue, 8 Jan 2013 21:41:34 -0600 Subject: [PATCH 040/237] Added test for cdata in sexp-as-element that contains an embedded ]]> (DXML-12) --- .../clojure/clojure/data/xml/test_sexp.clj | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/test/clojure/clojure/data/xml/test_sexp.clj b/src/test/clojure/clojure/data/xml/test_sexp.clj index 04ef20f..bccd988 100644 --- a/src/test/clojure/clojure/data/xml/test_sexp.clj +++ b/src/test/clojure/clojure/data/xml/test_sexp.clj @@ -34,15 +34,26 @@ (sexp-as-element sexp-input))))) (deftest with-multiple-cdata - (let [xml-input (element :tag {:attr "value"} - (element :body {} - (cdata "not parsed " + (let [xml-input (element :tag {:attr "value"} + (element :body {} + (cdata "not parsed more not parsed Date: Tue, 8 Jan 2013 21:51:17 -0600 Subject: [PATCH 041/237] Added a CHANGES.md file --- CHANGES.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 CHANGES.md diff --git a/CHANGES.md b/CHANGES.md new file mode 100644 index 0000000..a11243a --- /dev/null +++ b/CHANGES.md @@ -0,0 +1,7 @@ +From 0.0.6 to 0.0.7 + +- Fixed bug with args to the indentation function (DXML-7) +- Strings now supported as tag names, previously was only kewords (DXML-8) +- Add CDATA and comments support to sexp-as-element (DXML-11) +- data.xml now properly handles CDATA records that contain an embedded ]]> + by breaking it into two CDATA sections (DXML-12) \ No newline at end of file From d344dae465ab17a9440a9cc0ec1fb5b9cebc8562 Mon Sep 17 00:00:00 2001 From: "Hudson @ build.clojure.org" Date: Tue, 8 Jan 2013 22:04:23 -0600 Subject: [PATCH 042/237] [maven-release-plugin] prepare release data.xml-0.0.7 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3ad83e7..b2ee90b 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ 4.0.0 data.xml - 0.0.7-SNAPSHOT + 0.0.7 data.xml jar Functions to parse XML into lazy sequences and lazy trees and emit these as text From 4ec422ff147cf5cc3546d9a5779937fceb499586 Mon Sep 17 00:00:00 2001 From: "Hudson @ build.clojure.org" Date: Tue, 8 Jan 2013 22:04:23 -0600 Subject: [PATCH 043/237] [maven-release-plugin] prepare for next development iteration --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b2ee90b..b540304 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ 4.0.0 data.xml - 0.0.7 + 0.0.8-SNAPSHOT data.xml jar Functions to parse XML into lazy sequences and lazy trees and emit these as text From bfbb4c8c6fc2c8cf4cf75fd51d162bbd80cda927 Mon Sep 17 00:00:00 2001 From: Ryan Senior Date: Tue, 8 Jan 2013 22:09:31 -0600 Subject: [PATCH 044/237] Updated README to latest version (0.0.7) --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0f0f1f9..d01e8a1 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Please report bugs using JIRA [here](http://dev.clojure.org/jira/browse/DXML). ## Installation -Latest stable release: 0.0.6 +Latest stable release: 0.0.7 * [All Released Versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22data.xml%22) @@ -35,13 +35,13 @@ For Maven projects, add the following XML in your `pom.xml`'s `` s org.clojure data.xml - 0.0.6 + 0.0.7 ### Leiningen Add the following to the `project.clj` dependencies: - [org.clojure/data.xml "0.0.6"] + [org.clojure/data.xml "0.0.7"] ## Examples From 466d4025cd20481f6f2c28610aa4bc3d4c9ca2b8 Mon Sep 17 00:00:00 2001 From: Andy Fingerhut Date: Thu, 25 Apr 2013 11:29:12 -0700 Subject: [PATCH 045/237] DXML-16: Eliminate reflection in function emit-cdata Signed-off-by: Ryan Senior --- src/main/clojure/clojure/data/xml.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/clojure/data/xml.clj b/src/main/clojure/clojure/data/xml.clj index bf9c9cf..756b572 100644 --- a/src/main/clojure/clojure/data/xml.clj +++ b/src/main/clojure/clojure/data/xml.clj @@ -53,7 +53,7 @@ (or (nil? s) (= s ""))) -(defn emit-cdata [^String cdata-str writer] +(defn emit-cdata [^String cdata-str ^javax.xml.stream.XMLStreamWriter writer] (when-not (str-empty? cdata-str) (let [idx (.indexOf cdata-str "]]>")] (if (= idx -1) From e963a865d488497c7c54f96375310ff1fec8d70d Mon Sep 17 00:00:00 2001 From: Ryan Senior Date: Wed, 14 Aug 2013 23:36:09 -0500 Subject: [PATCH 046/237] Added an EPL license file (DXml-19) --- epl-v10.html | 261 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 261 insertions(+) create mode 100644 epl-v10.html diff --git a/epl-v10.html b/epl-v10.html new file mode 100644 index 0000000..813c07d --- /dev/null +++ b/epl-v10.html @@ -0,0 +1,261 @@ + + + + + + +Eclipse Public License - Version 1.0 + + + + + + +

Eclipse Public License - v 1.0

+ +

THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE +PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR +DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS +AGREEMENT.

+ +

1. DEFINITIONS

+ +

"Contribution" means:

+ +

a) in the case of the initial Contributor, the initial +code and documentation distributed under this Agreement, and

+

b) in the case of each subsequent Contributor:

+

i) changes to the Program, and

+

ii) additions to the Program;

+

where such changes and/or additions to the Program +originate from and are distributed by that particular Contributor. A +Contribution 'originates' from a Contributor if it was added to the +Program by such Contributor itself or anyone acting on such +Contributor's behalf. Contributions do not include additions to the +Program which: (i) are separate modules of software distributed in +conjunction with the Program under their own license agreement, and (ii) +are not derivative works of the Program.

+ +

"Contributor" means any person or entity that distributes +the Program.

+ +

"Licensed Patents" mean patent claims licensable by a +Contributor which are necessarily infringed by the use or sale of its +Contribution alone or when combined with the Program.

+ +

"Program" means the Contributions distributed in accordance +with this Agreement.

+ +

"Recipient" means anyone who receives the Program under +this Agreement, including all Contributors.

+ +

2. GRANT OF RIGHTS

+ +

a) Subject to the terms of this Agreement, each +Contributor hereby grants Recipient a non-exclusive, worldwide, +royalty-free copyright license to reproduce, prepare derivative works +of, publicly display, publicly perform, distribute and sublicense the +Contribution of such Contributor, if any, and such derivative works, in +source code and object code form.

+ +

b) Subject to the terms of this Agreement, each +Contributor hereby grants Recipient a non-exclusive, worldwide, +royalty-free patent license under Licensed Patents to make, use, sell, +offer to sell, import and otherwise transfer the Contribution of such +Contributor, if any, in source code and object code form. This patent +license shall apply to the combination of the Contribution and the +Program if, at the time the Contribution is added by the Contributor, +such addition of the Contribution causes such combination to be covered +by the Licensed Patents. The patent license shall not apply to any other +combinations which include the Contribution. No hardware per se is +licensed hereunder.

+ +

c) Recipient understands that although each Contributor +grants the licenses to its Contributions set forth herein, no assurances +are provided by any Contributor that the Program does not infringe the +patent or other intellectual property rights of any other entity. Each +Contributor disclaims any liability to Recipient for claims brought by +any other entity based on infringement of intellectual property rights +or otherwise. As a condition to exercising the rights and licenses +granted hereunder, each Recipient hereby assumes sole responsibility to +secure any other intellectual property rights needed, if any. For +example, if a third party patent license is required to allow Recipient +to distribute the Program, it is Recipient's responsibility to acquire +that license before distributing the Program.

+ +

d) Each Contributor represents that to its knowledge it +has sufficient copyright rights in its Contribution, if any, to grant +the copyright license set forth in this Agreement.

+ +

3. REQUIREMENTS

+ +

A Contributor may choose to distribute the Program in object code +form under its own license agreement, provided that:

+ +

a) it complies with the terms and conditions of this +Agreement; and

+ +

b) its license agreement:

+ +

i) effectively disclaims on behalf of all Contributors +all warranties and conditions, express and implied, including warranties +or conditions of title and non-infringement, and implied warranties or +conditions of merchantability and fitness for a particular purpose;

+ +

ii) effectively excludes on behalf of all Contributors +all liability for damages, including direct, indirect, special, +incidental and consequential damages, such as lost profits;

+ +

iii) states that any provisions which differ from this +Agreement are offered by that Contributor alone and not by any other +party; and

+ +

iv) states that source code for the Program is available +from such Contributor, and informs licensees how to obtain it in a +reasonable manner on or through a medium customarily used for software +exchange.

+ +

When the Program is made available in source code form:

+ +

a) it must be made available under this Agreement; and

+ +

b) a copy of this Agreement must be included with each +copy of the Program.

+ +

Contributors may not remove or alter any copyright notices contained +within the Program.

+ +

Each Contributor must identify itself as the originator of its +Contribution, if any, in a manner that reasonably allows subsequent +Recipients to identify the originator of the Contribution.

+ +

4. COMMERCIAL DISTRIBUTION

+ +

Commercial distributors of software may accept certain +responsibilities with respect to end users, business partners and the +like. While this license is intended to facilitate the commercial use of +the Program, the Contributor who includes the Program in a commercial +product offering should do so in a manner which does not create +potential liability for other Contributors. Therefore, if a Contributor +includes the Program in a commercial product offering, such Contributor +("Commercial Contributor") hereby agrees to defend and +indemnify every other Contributor ("Indemnified Contributor") +against any losses, damages and costs (collectively "Losses") +arising from claims, lawsuits and other legal actions brought by a third +party against the Indemnified Contributor to the extent caused by the +acts or omissions of such Commercial Contributor in connection with its +distribution of the Program in a commercial product offering. The +obligations in this section do not apply to any claims or Losses +relating to any actual or alleged intellectual property infringement. In +order to qualify, an Indemnified Contributor must: a) promptly notify +the Commercial Contributor in writing of such claim, and b) allow the +Commercial Contributor to control, and cooperate with the Commercial +Contributor in, the defense and any related settlement negotiations. The +Indemnified Contributor may participate in any such claim at its own +expense.

+ +

For example, a Contributor might include the Program in a commercial +product offering, Product X. That Contributor is then a Commercial +Contributor. If that Commercial Contributor then makes performance +claims, or offers warranties related to Product X, those performance +claims and warranties are such Commercial Contributor's responsibility +alone. Under this section, the Commercial Contributor would have to +defend claims against the other Contributors related to those +performance claims and warranties, and if a court requires any other +Contributor to pay any damages as a result, the Commercial Contributor +must pay those damages.

+ +

5. NO WARRANTY

+ +

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS +PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS +OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, +ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY +OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely +responsible for determining the appropriateness of using and +distributing the Program and assumes all risks associated with its +exercise of rights under this Agreement , including but not limited to +the risks and costs of program errors, compliance with applicable laws, +damage to or loss of data, programs or equipment, and unavailability or +interruption of operations.

+ +

6. DISCLAIMER OF LIABILITY

+ +

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT +NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING +WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR +DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED +HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

+ +

7. GENERAL

+ +

If any provision of this Agreement is invalid or unenforceable under +applicable law, it shall not affect the validity or enforceability of +the remainder of the terms of this Agreement, and without further action +by the parties hereto, such provision shall be reformed to the minimum +extent necessary to make such provision valid and enforceable.

+ +

If Recipient institutes patent litigation against any entity +(including a cross-claim or counterclaim in a lawsuit) alleging that the +Program itself (excluding combinations of the Program with other +software or hardware) infringes such Recipient's patent(s), then such +Recipient's rights granted under Section 2(b) shall terminate as of the +date such litigation is filed.

+ +

All Recipient's rights under this Agreement shall terminate if it +fails to comply with any of the material terms or conditions of this +Agreement and does not cure such failure in a reasonable period of time +after becoming aware of such noncompliance. If all Recipient's rights +under this Agreement terminate, Recipient agrees to cease use and +distribution of the Program as soon as reasonably practicable. However, +Recipient's obligations under this Agreement and any licenses granted by +Recipient relating to the Program shall continue and survive.

+ +

Everyone is permitted to copy and distribute copies of this +Agreement, but in order to avoid inconsistency the Agreement is +copyrighted and may only be modified in the following manner. The +Agreement Steward reserves the right to publish new versions (including +revisions) of this Agreement from time to time. No one other than the +Agreement Steward has the right to modify this Agreement. The Eclipse +Foundation is the initial Agreement Steward. The Eclipse Foundation may +assign the responsibility to serve as the Agreement Steward to a +suitable separate entity. Each new version of the Agreement will be +given a distinguishing version number. The Program (including +Contributions) may always be distributed subject to the version of the +Agreement under which it was received. In addition, after a new version +of the Agreement is published, Contributor may elect to distribute the +Program (including its Contributions) under the new version. Except as +expressly stated in Sections 2(a) and 2(b) above, Recipient receives no +rights or licenses to the intellectual property of any Contributor under +this Agreement, whether expressly, by implication, estoppel or +otherwise. All rights in the Program not expressly granted under this +Agreement are reserved.

+ +

This Agreement is governed by the laws of the State of New York and +the intellectual property laws of the United States of America. No party +to this Agreement will bring a legal action under this Agreement more +than one year after the cause of action arose. Each party waives its +rights to a jury trial in any resulting litigation.

+ + + + From 94dcb541fae20730635444bc6f7aeeaf1668adf0 Mon Sep 17 00:00:00 2001 From: Jeff Weiss Date: Thu, 20 Jun 2013 09:11:58 -0400 Subject: [PATCH 047/237] Fix handling of cdata end tags (issue DXML-17) Signed-off-by: Ryan Senior --- src/main/clojure/clojure/data/xml.clj | 4 ++-- src/test/clojure/clojure/data/xml/test_emit.clj | 8 ++++---- src/test/clojure/clojure/data/xml/test_sexp.clj | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/clojure/clojure/data/xml.clj b/src/main/clojure/clojure/data/xml.clj index 756b572..bbf61df 100644 --- a/src/main/clojure/clojure/data/xml.clj +++ b/src/main/clojure/clojure/data/xml.clj @@ -59,8 +59,8 @@ (if (= idx -1) (.writeCData writer cdata-str ) (do - (.writeCData writer (subs cdata-str 0 idx)) - (recur (subs cdata-str (+ idx 3)) writer)))))) + (.writeCData writer (subs cdata-str 0 (+ idx 2))) + (recur (subs cdata-str (+ idx 2)) writer)))))) (defn emit-event [event ^javax.xml.stream.XMLStreamWriter writer] (case (:type event) diff --git a/src/test/clojure/clojure/data/xml/test_emit.clj b/src/test/clojure/clojure/data/xml/test_emit.clj index eb29269..6e98d44 100644 --- a/src/test/clojure/clojure/data/xml/test_emit.clj +++ b/src/test/clojure/clojure/data/xml/test_emit.clj @@ -85,18 +85,18 @@ (cdata "")))))) (testing "cdata with ]]> chars" (is (= (str "" - "]]>]]>") + "]]]]>]]>") (emit-str (element :cdata-stuff {} (cdata "]]>")))))) (testing "cdata with ]]> chars and newlines" (is (= (str "" - "\n\n\n]]>]]>") + "\n\n\n]]]]>]]>") (emit-str (element :cdata-stuff {} (cdata "\n\n\n]]>"))))))) (deftest emitting-cdata-with-embedded-end (is (= (str "" - "]]>]]>") + "]]]]>]]>") (emit-str (element :cdata-stuff {} (cdata "]]>"))))) ) @@ -128,4 +128,4 @@ "\n \n foo\n \n \n\n") sw (java.io.StringWriter.)] (indent nested-xml sw :encoding "UTF-8") - (is (= expect (.toString sw))))) \ No newline at end of file + (is (= expect (.toString sw))))) diff --git a/src/test/clojure/clojure/data/xml/test_sexp.clj b/src/test/clojure/clojure/data/xml/test_sexp.clj index bccd988..0b89a00 100644 --- a/src/test/clojure/clojure/data/xml/test_sexp.clj +++ b/src/test/clojure/clojure/data/xml/test_sexp.clj @@ -47,8 +47,8 @@ (testing "cdata with embedded ]]>" (let [xml-input (element :tag {:attr "value"} (element :body {} - (cdata "not parsed more not parsed more not parsed Date: Sat, 7 Sep 2013 14:15:59 -0700 Subject: [PATCH 048/237] Update parent pom to 0.1.2 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b540304..6096b01 100644 --- a/pom.xml +++ b/pom.xml @@ -27,7 +27,7 @@ org.clojure pom.contrib - 0.0.25 + 0.1.2 From ffd6957baa0cf752fa0678be7f2a3393eab16739 Mon Sep 17 00:00:00 2001 From: Ryan Senior Date: Sun, 10 Nov 2013 21:51:21 -0600 Subject: [PATCH 049/237] Added support for emitting Booleans and Numbers (DXML-14) --- src/main/clojure/clojure/data/xml.clj | 14 +++++++++++++- src/test/clojure/clojure/data/xml/test_emit.clj | 14 ++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/clojure/data/xml.clj b/src/main/clojure/clojure/data/xml.clj index bbf61df..b7f9879 100644 --- a/src/main/clojure/clojure/data/xml.clj +++ b/src/main/clojure/clojure/data/xml.clj @@ -103,7 +103,19 @@ (Event. :chars nil nil s)) (next-events [_ next-items] next-items) - + + Boolean + (gen-event [b] + (Event. :chars nil nil (str b))) + (next-events [_ next-items] + next-items) + + Number + (gen-event [b] + (Event. :chars nil nil (str b))) + (next-events [_ next-items] + next-items) + CData (gen-event [cdata] (Event. :cdata nil nil (:content cdata))) diff --git a/src/test/clojure/clojure/data/xml/test_emit.clj b/src/test/clojure/clojure/data/xml/test_emit.clj index 6e98d44..f8d345f 100644 --- a/src/test/clojure/clojure/data/xml/test_emit.clj +++ b/src/test/clojure/clojure/data/xml/test_emit.clj @@ -129,3 +129,17 @@ sw (java.io.StringWriter.)] (indent nested-xml sw :encoding "UTF-8") (is (= expect (.toString sw))))) + +(deftest test-boolean + (is (= "true" + (emit-str (element :foo {} true))))) + +(deftest test-number + (is (= "1" + (emit-str (element :foo {} 1)))) + (is (= "1.2" + (emit-str (element :foo {} 1.2)))) + (is (= "0" + (emit-str (element :foo {} (int 0))))) + (is (= "1.2" + (emit-str (element :foo {} (float 1.2)))))) From da594a2a6041391684f56a5efdb51041d7d317ca Mon Sep 17 00:00:00 2001 From: Andy Fingerhut Date: Mon, 23 Dec 2013 15:48:45 -0800 Subject: [PATCH 050/237] DXML-21: Rename deftests so they all have unique names to avoid some tests being ignored completely. Signed-off-by: Ryan Senior --- src/test/clojure/clojure/data/xml/test_emit.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/clojure/clojure/data/xml/test_emit.clj b/src/test/clojure/clojure/data/xml/test_emit.clj index f8d345f..f04ad2c 100644 --- a/src/test/clojure/clojure/data/xml/test_emit.clj +++ b/src/test/clojure/clojure/data/xml/test_emit.clj @@ -35,7 +35,7 @@ "")] (is (= expect (emit-str deep-tree))))) -(deftest defaults +(deftest defaults-2 ;;XML below should be updated when namespace support is in (let [expect (str "done")] (is (= expect (emit-str (element "foo/bar" {"foo/item" 1} [(element "foo/baz" {"foo/item" 2} "done")])))))) @@ -122,7 +122,7 @@ "\n \n foo\n \n \n\n")] (is (= expect (indent-str nested-xml))))) -(deftest test-indent +(deftest test-indent-2 (let [nested-xml (lazy-parse* (str "foo")) expect (str "\n\n " "\n \n foo\n \n \n\n") From fe489d910f027a732a794fd75cd1963afd377fff Mon Sep 17 00:00:00 2001 From: Ryan Senior Date: Wed, 16 Apr 2014 16:09:18 -0500 Subject: [PATCH 051/237] Remove duplicate test-indent, collapsed defaults and defaults-2 tests into one --- .../clojure/clojure/data/xml/test_emit.clj | 41 ++++++++----------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/src/test/clojure/clojure/data/xml/test_emit.clj b/src/test/clojure/clojure/data/xml/test_emit.clj index f04ad2c..cb45288 100644 --- a/src/test/clojure/clojure/data/xml/test_emit.clj +++ b/src/test/clojure/clojure/data/xml/test_emit.clj @@ -8,7 +8,7 @@ (ns ^{:doc "Tests for emit to print XML text." :author "Chris Houser"} - clojure.data.xml.test-emit + clojure.data.xml.test-emit (:use clojure.test clojure.data.xml [clojure.data.xml.test-utils :only (test-stream lazy-parse*)])) @@ -24,22 +24,21 @@ ""))) (deftest defaults - (let [expect (str "" - "" - " t1t2" - " t3t4" - " t5t6" - " t7" - " t8t10t11" - " t12t13t14" - "")] - (is (= expect (emit-str deep-tree))))) - -(deftest defaults-2 - ;;XML below should be updated when namespace support is in - (let [expect (str "done")] - (is (= expect (emit-str (element "foo/bar" {"foo/item" 1} [(element "foo/baz" {"foo/item" 2} "done")])))))) - + (testing "basic parsing" + (let [expect (str "" + "" + " t1t2" + " t3t4" + " t5t6" + " t7" + " t8t10t11" + " t12t13t14" + "")] + (is (= expect (emit-str deep-tree))))) + + (testing "namespaced defaults" + (let [expect (str "done")] + (is (= expect (emit-str (element "foo/bar" {"foo/item" 1} [(element "foo/baz" {"foo/item" 2} "done")]))))))) (deftest mixed-quotes (is (= (lazy-parse* @@ -122,14 +121,6 @@ "\n \n foo\n \n \n\n")] (is (= expect (indent-str nested-xml))))) -(deftest test-indent-2 - (let [nested-xml (lazy-parse* (str "foo")) - expect (str "\n\n " - "\n \n foo\n \n \n\n") - sw (java.io.StringWriter.)] - (indent nested-xml sw :encoding "UTF-8") - (is (= expect (.toString sw))))) - (deftest test-boolean (is (= "true" (emit-str (element :foo {} true))))) From 391763441eb288a4e74d57c5e6104f4993708fdb Mon Sep 17 00:00:00 2001 From: Ryan Senior Date: Wed, 16 Apr 2014 16:10:12 -0500 Subject: [PATCH 052/237] Fixed the indentation tests for JDK 1.8 --- .../clojure/clojure/data/xml/test_emit.clj | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/test/clojure/clojure/data/xml/test_emit.clj b/src/test/clojure/clojure/data/xml/test_emit.clj index cb45288..a75ed80 100644 --- a/src/test/clojure/clojure/data/xml/test_emit.clj +++ b/src/test/clojure/clojure/data/xml/test_emit.clj @@ -105,21 +105,27 @@ (emit-str (element :comment-stuff {} "comment " (xml-comment " goes here ") - " not here")))) ) + " not here"))))) + +(def xml-decl-newline? + (-> (System/getProperty "java.version") + (.startsWith "1.8") + not)) (deftest test-indent (let [nested-xml (lazy-parse* (str "foo")) - expect (str "\n\n " - "\n \n foo\n \n \n\n") - sw (java.io.StringWriter.)] - (indent nested-xml sw) - (is (= expect (.toString sw))))) + expect (str "\n \n \n foo\n \n \n\n") + sw (java.io.StringWriter.) + _ (indent nested-xml sw) + result (.toString sw)] + (is (= expect + (subs result (.indexOf result "")))))) (deftest test-indent-str (let [nested-xml (lazy-parse* (str "foo")) - expect (str "\n\n " - "\n \n foo\n \n \n\n")] - (is (= expect (indent-str nested-xml))))) + expect (str "\n \n \n foo\n \n \n\n") + result (indent-str nested-xml)] + (is (= expect (subs result (.indexOf result "")))))) (deftest test-boolean (is (= "true" From 3645a811e0285e7b6a4d351fc016d78f36d867f2 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Tue, 20 May 2014 19:24:36 -0700 Subject: [PATCH 053/237] Add CONTRIBUTING.md --- CONTRIBUTING.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..8c5d243 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,14 @@ +This is a [Clojure contrib] project. + +Under the Clojure contrib [guidelines], this project cannot accept +pull requests. All patches must be submitted via [JIRA]. + +See [Contributing] and the [FAQ] on the Clojure development [wiki] for +more information on how to contribute. + +[Clojure contrib]: http://dev.clojure.org/display/doc/Clojure+Contrib +[Contributing]: http://dev.clojure.org/display/community/Contributing +[FAQ]: http://dev.clojure.org/display/community/Contributing+FAQ +[JIRA]: http://dev.clojure.org/jira/browse/DXML +[guidelines]: http://dev.clojure.org/display/community/Guidelines+for+Clojure+Contrib+committers +[wiki]: http://dev.clojure.org/ From 98905909bdfecb7cda44364232c8d38cc87244d9 Mon Sep 17 00:00:00 2001 From: puredanger Date: Mon, 11 Aug 2014 11:33:47 -0500 Subject: [PATCH 054/237] remove unnecessary snapshot repo from pom - this is added by the sonatype super poms in a profile when needed --- pom.xml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pom.xml b/pom.xml index 6096b01..8651036 100644 --- a/pom.xml +++ b/pom.xml @@ -36,10 +36,4 @@ git@github.com:clojure/data.xml.git - - - sonatype-oss-snapshots - https://oss.sonatype.org/content/repositories/snapshots - -
From acb9712f7e1be5008f543004a7b1997f6bfaad41 Mon Sep 17 00:00:00 2001 From: "Hudson @ build.clojure.org" Date: Mon, 11 Aug 2014 11:38:07 -0500 Subject: [PATCH 055/237] [maven-release-plugin] prepare release data.xml-0.0.8 --- pom.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8651036..f5e3fd8 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ 4.0.0 data.xml - 0.0.8-SNAPSHOT + 0.0.8 data.xml jar Functions to parse XML into lazy sequences and lazy trees and emit these as text @@ -34,6 +34,7 @@ scm:git:git@github.com:clojure/data.xml.git scm:git:git@github.com:clojure/data.xml.git git@github.com:clojure/data.xml.git + data.xml-0.0.8 From b07bc1cbac02567621d2752882f6add8bb904e09 Mon Sep 17 00:00:00 2001 From: "Hudson @ build.clojure.org" Date: Mon, 11 Aug 2014 11:38:07 -0500 Subject: [PATCH 056/237] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index f5e3fd8..1895259 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ 4.0.0 data.xml - 0.0.8 + 0.0.9-SNAPSHOT data.xml jar Functions to parse XML into lazy sequences and lazy trees and emit these as text @@ -34,7 +34,7 @@ scm:git:git@github.com:clojure/data.xml.git scm:git:git@github.com:clojure/data.xml.git git@github.com:clojure/data.xml.git - data.xml-0.0.8 + HEAD From 320eda8123d6b3d1a5d3c6875b46d21959eb4988 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Mon, 11 Aug 2014 11:40:22 -0500 Subject: [PATCH 057/237] Updated latest version --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d01e8a1..a7a5360 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Please report bugs using JIRA [here](http://dev.clojure.org/jira/browse/DXML). ## Installation -Latest stable release: 0.0.7 +Latest stable release: 0.0.8 * [All Released Versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22data.xml%22) @@ -35,13 +35,13 @@ For Maven projects, add the following XML in your `pom.xml`'s `` s org.clojure data.xml - 0.0.7 + 0.0.8 ### Leiningen Add the following to the `project.clj` dependencies: - [org.clojure/data.xml "0.0.7"] + [org.clojure/data.xml "0.0.8"] ## Examples From 14d0af1cef8c8af497a2602dc710ef65967537b6 Mon Sep 17 00:00:00 2001 From: Carlo Sciolla Date: Wed, 27 Aug 2014 23:03:03 +0200 Subject: [PATCH 058/237] Prevent XXE attacks by disabling external entities resolution in the default parser Signed-off-by: Ryan Senior --- src/main/clojure/clojure/data/xml.clj | 25 +++++----- .../clojure/data/xml/test_entities.clj | 49 +++++++++++++++++++ src/test/resources/secret.txt | 1 + 3 files changed, 64 insertions(+), 11 deletions(-) create mode 100644 src/test/clojure/clojure/data/xml/test_entities.clj create mode 100644 src/test/resources/secret.txt diff --git a/src/main/clojure/clojure/data/xml.clj b/src/main/clojure/clojure/data/xml.clj index b7f9879..e01f640 100644 --- a/src/main/clojure/clojure/data/xml.clj +++ b/src/main/clojure/clojure/data/xml.clj @@ -54,7 +54,7 @@ (= s ""))) (defn emit-cdata [^String cdata-str ^javax.xml.stream.XMLStreamWriter writer] - (when-not (str-empty? cdata-str) + (when-not (str-empty? cdata-str) (let [idx (.indexOf cdata-str "]]>")] (if (= idx -1) (.writeCData writer cdata-str ) @@ -97,7 +97,7 @@ (if-let [r (seq (rest coll))] (cons (next-events (first coll) r) next-items) (next-events (first coll) next-items))) - + String (gen-event [s] (Event. :chars nil nil s)) @@ -121,13 +121,13 @@ (Event. :cdata nil nil (:content cdata))) (next-events [_ next-items] next-items) - + Comment (gen-event [comment] (Event. :comment nil nil (:content comment))) (next-events [_ next-items] next-items) - + nil (gen-event [_] (Event. :chars nil nil "")) @@ -279,7 +279,7 @@ ; Note, sreader is mutable and mutated here in pull-seq, but it's ; protected by a lazy-seq so it's thread-safe. (defn- pull-seq - "Creates a seq of events. The XMLStreamConstants/SPACE clause below doesn't seem to + "Creates a seq of events. The XMLStreamConstants/SPACE clause below doesn't seem to be triggered by the JDK StAX parser, but is by others. Leaving in to be more complete." [^XMLStreamReader sreader] (lazy-seq @@ -289,7 +289,7 @@ (cons (event :start-element (keyword (.getLocalName sreader)) (attr-hash sreader) nil) - (pull-seq sreader)) + (pull-seq sreader)) XMLStreamConstants/END_ELEMENT (cons (event :end-element (keyword (.getLocalName sreader)) nil nil) @@ -327,9 +327,13 @@ "Parses the XML InputSource source using a pull-parser. Returns a lazy sequence of Event records. Accepts key pairs with XMLInputFactory options, see http://docs.oracle.com/javase/6/docs/api/javax/xml/stream/XMLInputFactory.html - and xml-input-factory-props for more information. Defaults coalescing true." + and xml-input-factory-props for more information. + Defaults coalescing true and supporting-external-entities false." [s & {:as props}] - (let [fac (new-xml-input-factory (merge {:coalescing true} props)) + (let [merged-props (merge {:coalescing true + :supporting-external-entities false} + props) + fac (new-xml-input-factory merged-props) ;; Reflection on following line cannot be eliminated via a ;; type hint, because s is advertised by fn parse to be an ;; InputStream or Reader, and there are different @@ -373,7 +377,7 @@ (when (instance? java.io.OutputStreamWriter stream) (check-stream-encoding stream (or (:encoding opts) "UTF-8"))) - + (.writeStartDocument writer (or (:encoding opts) "UTF-8") "1.0") (doseq [event (flatten-elements [e])] (emit-event event writer)) @@ -395,7 +399,7 @@ (defn indent "Emits the XML and indents the result. WARNING: this is slow - it will emit the XML and read it in again to indent it. Intended for + it will emit the XML and read it in again to indent it. Intended for debugging/testing only." [e ^java.io.Writer stream & {:as opts}] (let [sw (java.io.StringWriter.) @@ -410,4 +414,3 @@ (let [^java.io.StringWriter sw (java.io.StringWriter.)] (indent e sw) (.toString sw))) - diff --git a/src/test/clojure/clojure/data/xml/test_entities.clj b/src/test/clojure/clojure/data/xml/test_entities.clj new file mode 100644 index 0000000..a2e80a0 --- /dev/null +++ b/src/test/clojure/clojure/data/xml/test_entities.clj @@ -0,0 +1,49 @@ +; Copyright (c) Rich Hickey. 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 ^{:doc "Test that external entities are not resolved by default, see https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Processing" + :author "Carlo Sciolla"} + clojure.data.xml.test-entities + (:use clojure.test + clojure.data.xml) + (:require [clojure.java.io :as io])) + +(defn vulnerable-input + "Creates an XML with an external entity referring to the given URL" + [file-url] + (str "" + "" + " ]>" + "&xxe;")) + +(defn secret-file + "Returns the URL to the secret file containing the server root password" + [] + (io/resource "secret.txt")) + +(defn parse-vulnerable-file + "Parses the vulnerable file, optionally passing the given options to the parser" + ([] (parse-str (vulnerable-input (secret-file)))) + ([& options] (apply parse-str (vulnerable-input (secret-file)) options))) + +(deftest prevent-xxe-by-default + (testing "To prevent XXE attacks, exernal entities by default resolve to nil" + (let [parsed (parse-vulnerable-file) + expected #clojure.data.xml.Element{:tag :foo + :attrs {} + :content ()}] + (is (= expected parsed))))) + +(deftest allow-external-entities-if-required + (testing "If explicitly enabled, external entities are property resolved" + (let [parsed (parse-vulnerable-file :supporting-external-entities true) + expected #clojure.data.xml.Element{:tag :foo + :attrs {} + :content ("root_password\n")}] + (is (= expected parsed))))) diff --git a/src/test/resources/secret.txt b/src/test/resources/secret.txt new file mode 100644 index 0000000..9a645d0 --- /dev/null +++ b/src/test/resources/secret.txt @@ -0,0 +1 @@ +root_password From f66bbe05f9a796acc1c258c1bc4d2dcabaa673e7 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Sun, 10 May 2015 01:36:31 +0200 Subject: [PATCH 059/237] Add *~ to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index fd7ca27..5cdab64 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target/ modules/xml/target/ modules/xml.pull-parser/target/ +*~ From 37b6fee0929bfea0bafafb177b52b953b116ce01 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Sun, 10 May 2015 01:39:35 +0200 Subject: [PATCH 060/237] Move node and element deftypes + constructors into their own ns --- src/main/clojure/clojure/data/xml.clj | 24 +++++----- src/main/clojure/clojure/data/xml/event.clj | 22 +++++++++ src/main/clojure/clojure/data/xml/impl.clj | 23 +++++++++ src/main/clojure/clojure/data/xml/node.clj | 48 +++++++++++++++++++ .../clojure/data/xml/test_entities.clj | 12 ++--- 5 files changed, 110 insertions(+), 19 deletions(-) create mode 100644 src/main/clojure/clojure/data/xml/event.clj create mode 100644 src/main/clojure/clojure/data/xml/impl.clj create mode 100644 src/main/clojure/clojure/data/xml/node.clj diff --git a/src/main/clojure/clojure/data/xml.clj b/src/main/clojure/clojure/data/xml.clj index e01f640..61a1406 100644 --- a/src/main/clojure/clojure/data/xml.clj +++ b/src/main/clojure/clojure/data/xml.clj @@ -10,19 +10,22 @@ emit these as text." :author "Chris Houser"} clojure.data.xml - (:require [clojure.string :as str]) + (:require [clojure.string :as str] + (clojure.data.xml + ;; re-exported names are not referred here, but by export-api, next + [event :as event] + [node :as node] + [impl :as impl])) (:import (javax.xml.stream XMLInputFactory XMLStreamReader XMLStreamConstants) (java.nio.charset Charset) - (java.io Reader))) + (java.io Reader) + clojure.data.xml.event.Event + (clojure.data.xml.node CData Comment Element))) -; Represents a parse event. -; type is one of :start-element, :end-element, or :characters -(defrecord Event [type name attrs str]) - -(defn event [type name & [attrs str]] - (Event. type name attrs str)) +(impl/export-api + event/event node/element node/element* node/cdata node/xml-comment) (defn qualified-name [event-name] (if (instance? clojure.lang.Named event-name) @@ -39,11 +42,6 @@ (.writeAttribute writer attr-ns attr-name (str v)) (.writeAttribute writer attr-name (str v)))))) -; Represents a node of an XML tree -(defrecord Element [tag attrs content]) -(defrecord CData [content]) -(defrecord Comment [content]) - (defn emit-start-tag [event ^javax.xml.stream.XMLStreamWriter writer] (let [[nspace qname] (qualified-name (:name event))] (.writeStartElement writer "" qname (or nspace "")) diff --git a/src/main/clojure/clojure/data/xml/event.clj b/src/main/clojure/clojure/data/xml/event.clj new file mode 100644 index 0000000..9f01cdf --- /dev/null +++ b/src/main/clojure/clojure/data/xml/event.clj @@ -0,0 +1,22 @@ +; Copyright (c) Rich Hickey. 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.event + "Data type for xml pull events" + {:author "Herwig Hochleitner"}) + +; Represents a parse event. +; type is one of :start-element, :end-element, or :characters +(defrecord Event [type name attrs str]) + +(defn event + ([type name] (Event. type name nil nil)) + ([type name attrs] (Event. type name attrs nil)) + ([type name attrs str] (Event. type name attrs str)) + ([type name attrs str meta] (Event. type name attrs str meta nil))) + diff --git a/src/main/clojure/clojure/data/xml/impl.clj b/src/main/clojure/clojure/data/xml/impl.clj new file mode 100644 index 0000000..8f16eb2 --- /dev/null +++ b/src/main/clojure/clojure/data/xml/impl.clj @@ -0,0 +1,23 @@ +; Copyright (c) Rich Hickey. 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.impl + "Shared private code for data.xml namespaces" + {:author "Herwig Hochleitner"}) + +(defn- export-form [var-name] + (let [vsym (symbol (name var-name))] + `[(def ~vsym ~var-name) + (alter-meta! (var ~vsym) + (constantly (assoc (meta (var ~var-name)) + :wrapped-by (var ~vsym))))])) + +(defmacro export-api + "This creates vars, that take their (local) name, value and metadata from another var" + [& names] + (cons 'do (mapcat export-form names))) diff --git a/src/main/clojure/clojure/data/xml/node.clj b/src/main/clojure/clojure/data/xml/node.clj new file mode 100644 index 0000000..d2a6e2a --- /dev/null +++ b/src/main/clojure/clojure/data/xml/node.clj @@ -0,0 +1,48 @@ +; Copyright (c) Rich Hickey. 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.node + "Data types for xml nodes: Element, CData and Comment" + {:author "Herwig Hochleitner"}) + +;; Parsed data format +;; Represents a node of an XML tree +(defrecord Element [tag attrs content]) +(defrecord CData [content]) +(defrecord Comment [content]) + +(defn element* + "Create an xml element from a content collection and optional metadata" + ([tag attrs content meta] + (Element. tag (or attrs {}) (remove nil? content) meta nil)) + ([tag attrs content] + (Element. tag (or attrs {}) (remove nil? content)))) + +;; Compiler macro for inlining the two constructors +(alter-meta! #'element* assoc :inline + (fn + ([tag attrs content meta] + `(Element. ~tag (or ~attrs {}) (remove nil? ~content) ~meta nil)) + ([tag attrs content] + `(Element. ~tag (or ~attrs {}) (remove nil? ~content))))) + +(defn element + "Create an xml Element from content varargs" + ([tag] (element* tag nil nil)) + ([tag attrs] (element* tag attrs nil)) + ([tag attrs & content] (element* tag attrs content))) + +(definline cdata + "Create a CData node" + [content] + `(CData. ~content)) + +(definline xml-comment + "Create a Comment node" + [content] + `(Comment. ~content)) diff --git a/src/test/clojure/clojure/data/xml/test_entities.clj b/src/test/clojure/clojure/data/xml/test_entities.clj index a2e80a0..42ae214 100644 --- a/src/test/clojure/clojure/data/xml/test_entities.clj +++ b/src/test/clojure/clojure/data/xml/test_entities.clj @@ -35,15 +35,15 @@ (deftest prevent-xxe-by-default (testing "To prevent XXE attacks, exernal entities by default resolve to nil" (let [parsed (parse-vulnerable-file) - expected #clojure.data.xml.Element{:tag :foo - :attrs {} - :content ()}] + expected #clojure.data.xml.node.Element{:tag :foo + :attrs {} + :content ()}] (is (= expected parsed))))) (deftest allow-external-entities-if-required (testing "If explicitly enabled, external entities are property resolved" (let [parsed (parse-vulnerable-file :supporting-external-entities true) - expected #clojure.data.xml.Element{:tag :foo - :attrs {} - :content ("root_password\n")}] + expected #clojure.data.xml.node.Element{:tag :foo + :attrs {} + :content ("root_password\n")}] (is (= expected parsed))))) From 3d231d6be9b70e829817e1415297ac0dca98a647 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Tue, 22 Sep 2015 06:17:02 +0200 Subject: [PATCH 061/237] Refactoring and ns-clause cleanup Split up monolithic xml.clj This changes the event format towards a record per event There are rudiments of namespace awareness, but commented out --- src/main/clojure/clojure/data/xml.clj | 421 +++--------------- src/main/clojure/clojure/data/xml/event.clj | 80 +++- src/main/clojure/clojure/data/xml/impl.clj | 34 +- .../clojure/clojure/data/xml/jvm/emit.clj | 136 ++++++ .../clojure/clojure/data/xml/jvm/parse.clj | 98 ++++ .../clojure/clojure/data/xml/jvm/pprint.clj | 27 ++ .../clojure/clojure/data/xml/protocols.clj | 26 ++ src/main/clojure/clojure/data/xml/prxml.clj | 76 ++++ src/main/clojure/clojure/data/xml/tree.clj | 71 +++ .../clojure/clojure/data/xml/test_emit.clj | 7 +- .../clojure/data/xml/test_entities.clj | 8 +- .../clojure/clojure/data/xml/test_fresh.clj | 4 + .../clojure/clojure/data/xml/test_names.clj | 19 + .../clojure/clojure/data/xml/test_parse.clj | 9 +- .../clojure/clojure/data/xml/test_pprint.clj | 27 ++ .../clojure/data/xml/test_seq_tree.clj | 4 +- .../clojure/clojure/data/xml/test_sexp.clj | 7 +- .../clojure/clojure/data/xml/test_utils.clj | 4 +- 18 files changed, 659 insertions(+), 399 deletions(-) create mode 100644 src/main/clojure/clojure/data/xml/jvm/emit.clj create mode 100644 src/main/clojure/clojure/data/xml/jvm/parse.clj create mode 100644 src/main/clojure/clojure/data/xml/jvm/pprint.clj create mode 100644 src/main/clojure/clojure/data/xml/protocols.clj create mode 100644 src/main/clojure/clojure/data/xml/prxml.clj create mode 100644 src/main/clojure/clojure/data/xml/tree.clj create mode 100644 src/test/clojure/clojure/data/xml/test_fresh.clj create mode 100644 src/test/clojure/clojure/data/xml/test_names.clj create mode 100644 src/test/clojure/clojure/data/xml/test_pprint.clj diff --git a/src/main/clojure/clojure/data/xml.clj b/src/main/clojure/clojure/data/xml.clj index 61a1406..6fcb545 100644 --- a/src/main/clojure/clojure/data/xml.clj +++ b/src/main/clojure/clojure/data/xml.clj @@ -8,407 +8,86 @@ (ns ^{:doc "Functions to parse XML into lazy sequences and lazy trees and emit these as text." - :author "Chris Houser"} - clojure.data.xml - (:require [clojure.string :as str] - (clojure.data.xml - ;; re-exported names are not referred here, but by export-api, next - [event :as event] - [node :as node] - [impl :as impl])) - (:import (javax.xml.stream XMLInputFactory - XMLStreamReader - XMLStreamConstants) - (java.nio.charset Charset) - (java.io Reader) - clojure.data.xml.event.Event - (clojure.data.xml.node CData Comment Element))) - -(impl/export-api - event/event node/element node/element* node/cdata node/xml-comment) - -(defn qualified-name [event-name] - (if (instance? clojure.lang.Named event-name) - [(namespace event-name) (name event-name)] - (let [name-parts (str/split event-name #"/" 2)] - (if (= 2 (count name-parts)) - name-parts - [nil (first name-parts)])))) - -(defn write-attributes [attrs ^javax.xml.stream.XMLStreamWriter writer] - (doseq [[k v] attrs] - (let [[attr-ns attr-name] (qualified-name k)] - (if attr-ns - (.writeAttribute writer attr-ns attr-name (str v)) - (.writeAttribute writer attr-name (str v)))))) - -(defn emit-start-tag [event ^javax.xml.stream.XMLStreamWriter writer] - (let [[nspace qname] (qualified-name (:name event))] - (.writeStartElement writer "" qname (or nspace "")) - (write-attributes (:attrs event) writer))) - -(defn str-empty? [s] - (or (nil? s) - (= s ""))) - -(defn emit-cdata [^String cdata-str ^javax.xml.stream.XMLStreamWriter writer] - (when-not (str-empty? cdata-str) - (let [idx (.indexOf cdata-str "]]>")] - (if (= idx -1) - (.writeCData writer cdata-str ) - (do - (.writeCData writer (subs cdata-str 0 (+ idx 2))) - (recur (subs cdata-str (+ idx 2)) writer)))))) - -(defn emit-event [event ^javax.xml.stream.XMLStreamWriter writer] - (case (:type event) - :start-element (emit-start-tag event writer) - :end-element (.writeEndElement writer) - :chars (.writeCharacters writer (:str event)) - :cdata (emit-cdata (:str event) writer) - :comment (.writeComment writer (:str event)))) - -(defprotocol EventGeneration - "Protocol for generating new events based on element type" - (gen-event [item] - "Function to generate an event for e.") - (next-events [item next-items] - "Returns the next set of events that should occur after e. next-events are the - events that should be generated after this one is complete.")) - -(extend-protocol EventGeneration - Element - (gen-event [element] - (Event. :start-element (:tag element) (:attrs element) nil)) - (next-events [element next-items] - (cons (:content element) - (cons (Event. :end-element (:tag element) nil nil) next-items))) - Event - (gen-event [event] event) - (next-events [_ next-items] - next-items) - - clojure.lang.Sequential - (gen-event [coll] - (gen-event (first coll))) - (next-events [coll next-items] - (if-let [r (seq (rest coll))] - (cons (next-events (first coll) r) next-items) - (next-events (first coll) next-items))) - - String - (gen-event [s] - (Event. :chars nil nil s)) - (next-events [_ next-items] - next-items) - - Boolean - (gen-event [b] - (Event. :chars nil nil (str b))) - (next-events [_ next-items] - next-items) - - Number - (gen-event [b] - (Event. :chars nil nil (str b))) - (next-events [_ next-items] - next-items) - - CData - (gen-event [cdata] - (Event. :cdata nil nil (:content cdata))) - (next-events [_ next-items] - next-items) - - Comment - (gen-event [comment] - (Event. :comment nil nil (:content comment))) - (next-events [_ next-items] - next-items) - - nil - (gen-event [_] - (Event. :chars nil nil "")) - (next-events [_ next-items] - next-items)) - -(defn flatten-elements [elements] - (when (seq elements) - (lazy-seq - (let [e (first elements)] - (cons (gen-event e) - (flatten-elements (next-events e (rest elements)))))))) - -(defn element [tag & [attrs & content]] - (Element. tag (or attrs {}) (remove nil? content))) - -(defn cdata [content] - (CData. content)) - -(defn xml-comment [content] - (Comment. content)) - -;=== Parse-related functions === -(defn seq-tree - "Takes a seq of events that logically represents - a tree by each event being one of: enter-sub-tree event, - exit-sub-tree event, or node event. - - Returns a lazy sequence whose first element is a sequence of - sub-trees and whose remaining elements are events that are not - siblings or descendants of the initial event. - - The given exit? function must return true for any exit-sub-tree - event. parent must be a function of two arguments: the first is an - event, the second a sequence of nodes or subtrees that are children - of the event. parent must return nil or false if the event is not - an enter-sub-tree event. Any other return value will become - a sub-tree of the output tree and should normally contain in some - way the children passed as the second arg. The node function is - called with a single event arg on every event that is neither parent - nor exit, and its return value will become a node of the output tree. - - (seq-tree #(when (= %1 :<) (vector %2)) #{:>} str - [1 2 :< 3 :< 4 :> :> 5 :> 6]) - ;=> ((\"1\" \"2\" [(\"3\" [(\"4\")])] \"5\") 6)" - [parent exit? node coll] - (lazy-seq - (when-let [[event] (seq coll)] - (let [more (rest coll)] - (if (exit? event) - (cons nil more) - (let [tree (seq-tree parent exit? node more)] - (if-let [p (parent event (lazy-seq (first tree)))] - (let [subtree (seq-tree parent exit? node (lazy-seq (rest tree)))] - (cons (cons p (lazy-seq (first subtree))) - (lazy-seq (rest subtree)))) - (cons (cons (node event) (lazy-seq (first tree))) - (lazy-seq (rest tree)))))))))) + :author "Chris Houser"} -(defn event-tree - "Returns a lazy tree of Element objects for the given seq of Event - objects. See source-seq and parse." - [events] - (ffirst - (seq-tree - (fn [^Event event contents] - (when (= :start-element (.type event)) - (Element. (.name event) (.attrs event) contents))) - (fn [^Event event] (= :end-element (.type event))) - (fn [^Event event] (.str event)) - events))) - -(defprotocol AsElements - (as-elements [expr] "Return a seq of elements represented by an expression.")) - -(defn sexp-element [tag attrs child] - (cond - (= :-cdata tag) (CData. (first child)) - (= :-comment tag) (Comment. (first child)) - :else (Element. tag attrs (mapcat as-elements child)))) - -(extend-protocol AsElements - clojure.lang.IPersistentVector - (as-elements [v] - (let [[tag & [attrs & after-attrs :as content]] v - [attrs content] (if (map? attrs) - [(into {} (for [[k v] attrs] - [k (str v)])) - after-attrs] - [{} content])] - [(sexp-element tag attrs content)])) - - clojure.lang.ISeq - (as-elements [s] - (mapcat as-elements s)) - - clojure.lang.Keyword - (as-elements [k] - [(Element. k {} ())]) - - java.lang.String - (as-elements [s] - [s]) - - nil - (as-elements [_] nil) - - java.lang.Object - (as-elements [o] - [(str o)])) - -(defn sexps-as-fragment - "Convert a compact prxml/hiccup-style data structure into the more formal - tag/attrs/content format. A seq of elements will be returned, which may - not be suitable for immediate use as there is no root element. See also - sexp-as-element. - - The format is [:tag-name attr-map? content*]. Each vector opens a new tag; - seqs do not open new tags, and are just used for inserting groups of elements - into the parent tag. A bare keyword not in a vector creates an empty element. - - To provide XML conversion for your own data types, extend the AsElements - protocol to them." - ([] nil) - ([sexp] (as-elements sexp)) - ([sexp & sexps] (mapcat as-elements (cons sexp sexps)))) - -(defn sexp-as-element - "Convert a single sexp into an Element" - [sexp] - (let [[root & more] (sexps-as-fragment sexp)] - (when more - (throw - (IllegalArgumentException. - "Cannot have multiple root elements; try creating a fragment instead"))) - root)) - - -(defn- attr-prefix [^XMLStreamReader sreader index] - (let [p (.getAttributePrefix sreader index)] - (when-not (str/blank? p) - p))) - -(defn- attr-hash [^XMLStreamReader sreader] (into {} - (for [i (range (.getAttributeCount sreader))] - [(keyword (attr-prefix sreader i) (.getAttributeLocalName sreader i)) - (.getAttributeValue sreader i)]))) - -; Note, sreader is mutable and mutated here in pull-seq, but it's -; protected by a lazy-seq so it's thread-safe. -(defn- pull-seq - "Creates a seq of events. The XMLStreamConstants/SPACE clause below doesn't seem to - be triggered by the JDK StAX parser, but is by others. Leaving in to be more complete." - [^XMLStreamReader sreader] - (lazy-seq - (loop [] - (condp == (.next sreader) - XMLStreamConstants/START_ELEMENT - (cons (event :start-element - (keyword (.getLocalName sreader)) - (attr-hash sreader) nil) - (pull-seq sreader)) - XMLStreamConstants/END_ELEMENT - (cons (event :end-element - (keyword (.getLocalName sreader)) nil nil) - (pull-seq sreader)) - XMLStreamConstants/CHARACTERS - (if-let [text (and (not (.isWhiteSpace sreader)) - (.getText sreader))] - (cons (event :characters nil nil text) - (pull-seq sreader)) - (recur)) - XMLStreamConstants/END_DOCUMENT - nil - (recur);; Consume and ignore comments, spaces, processing instructions etc - )))) - -(def ^{:private true} xml-input-factory-props - {:allocator javax.xml.stream.XMLInputFactory/ALLOCATOR - :coalescing javax.xml.stream.XMLInputFactory/IS_COALESCING - :namespace-aware javax.xml.stream.XMLInputFactory/IS_NAMESPACE_AWARE - :replacing-entity-references javax.xml.stream.XMLInputFactory/IS_REPLACING_ENTITY_REFERENCES - :supporting-external-entities javax.xml.stream.XMLInputFactory/IS_SUPPORTING_EXTERNAL_ENTITIES - :validating javax.xml.stream.XMLInputFactory/IS_VALIDATING - :reporter javax.xml.stream.XMLInputFactory/REPORTER - :resolver javax.xml.stream.XMLInputFactory/RESOLVER - :support-dtd javax.xml.stream.XMLInputFactory/SUPPORT_DTD}) - -(defn- new-xml-input-factory [props] - (let [fac (javax.xml.stream.XMLInputFactory/newInstance)] - (doseq [[k v] props - :let [prop (xml-input-factory-props k)]] - (.setProperty fac prop v)) - fac)) - -(defn source-seq + clojure.data.xml + + (:require + (clojure.data.xml + [impl :refer [export-api]] + [node :as node] + [prxml :as prxml] + #_[name :as name]) + (clojure.data.xml.jvm + [pprint :refer + [indent-xml]] + [parse :refer + [pull-seq string-source make-stream-reader]] + [emit :refer + [write-document string-writer]]) + + [clojure.data.xml.tree :refer + [event-tree flatten-elements]])) + +(export-api node/element* node/element node/cdata node/xml-comment node/to-element + prxml/sexp-as-element prxml/sexps-as-fragment + ;name/parse-qname name/qname-uri name/qname-local name/to-qname name/ns-uri name/uri-ns name/declare-ns name/alias-ns + ) + +(defn event-seq "Parses the XML InputSource source using a pull-parser. Returns a lazy sequence of Event records. Accepts key pairs with XMLInputFactory options, see http://docs.oracle.com/javase/6/docs/api/javax/xml/stream/XMLInputFactory.html and xml-input-factory-props for more information. - Defaults coalescing true and supporting-external-entities false." - [s & {:as props}] - (let [merged-props (merge {:coalescing true - :supporting-external-entities false} - props) - fac (new-xml-input-factory merged-props) - ;; Reflection on following line cannot be eliminated via a - ;; type hint, because s is advertised by fn parse to be an - ;; InputStream or Reader, and there are different - ;; createXMLStreamReader signatures for each of those types. - sreader (.createXMLStreamReader fac s)] - (pull-seq sreader))) + Defaults coalescing true and supporting-external-entities false. + :include-node? can be a set of #{:start-element :end-element :characters :comment}" + [source {:as props}] + (let [props* (merge {:include-node? #{:element :characters} + :coalescing true + :supporting-external-entities false} + props)] + (pull-seq (make-stream-reader props* source) + (get props* :include-node?)))) (defn parse "Parses the source, which can be an InputStream or Reader, and returns a lazy tree of Element records. Accepts key pairs with XMLInputFactory options, see http://docs.oracle.com/javase/6/docs/api/javax/xml/stream/XMLInputFactory.html and xml-input-factory-props for more information. Defaults coalescing true." - [source & props] - (event-tree (apply source-seq source props))) + [source & opts] + (event-tree (event-seq source opts))) (defn parse-str "Parses the passed in string to Clojure data structures. Accepts key pairs with XMLInputFactory options, see http://docs.oracle.com/javase/6/docs/api/javax/xml/stream/XMLInputFactory.html and xml-input-factory-props for more information. Defaults coalescing true." - [s & props] - (let [sr (java.io.StringReader. s)] - (apply parse sr props))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;;; XML Emitting -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defn check-stream-encoding [^java.io.OutputStreamWriter stream xml-encoding] - (when (not= (Charset/forName xml-encoding) (Charset/forName (.getEncoding stream))) - (throw (Exception. (str "Output encoding of stream (" xml-encoding - ") doesn't match declaration (" - (.getEncoding stream) ")"))))) + [s & opts] + (apply parse (string-source s) opts)) (defn emit "Prints the given Element tree as XML text to stream. Options: :encoding Character encoding to use" - [e ^java.io.Writer stream & {:as opts}] - (let [^javax.xml.stream.XMLStreamWriter writer (-> (javax.xml.stream.XMLOutputFactory/newInstance) - (.createXMLStreamWriter stream))] - - (when (instance? java.io.OutputStreamWriter stream) - (check-stream-encoding stream (or (:encoding opts) "UTF-8"))) - - (.writeStartDocument writer (or (:encoding opts) "UTF-8") "1.0") - (doseq [event (flatten-elements [e])] - (emit-event event writer)) - (.writeEndDocument writer) - stream)) + [e writer & {:as opts}] + (write-document writer (flatten-elements [e]) opts)) (defn emit-str "Emits the Element to String and returns it" - [e] - (let [^java.io.StringWriter sw (java.io.StringWriter.)] - (emit e sw) - (.toString sw))) - -(defn ^javax.xml.transform.Transformer indenting-transformer [] - (doto (-> (javax.xml.transform.TransformerFactory/newInstance) .newTransformer) - (.setOutputProperty (javax.xml.transform.OutputKeys/INDENT) "yes") - (.setOutputProperty (javax.xml.transform.OutputKeys/METHOD) "xml") - (.setOutputProperty "{http://xml.apache.org/xslt}indent-amount" "2"))) + ([e & opts] + (let [sw (string-writer)] + (apply emit e sw opts) + (str sw)))) (defn indent "Emits the XML and indents the result. WARNING: this is slow it will emit the XML and read it in again to indent it. Intended for debugging/testing only." - [e ^java.io.Writer stream & {:as opts}] - (let [sw (java.io.StringWriter.) - _ (apply emit e sw (apply concat opts)) - source (-> sw .toString java.io.StringReader. javax.xml.transform.stream.StreamSource.) - result (javax.xml.transform.stream.StreamResult. stream)] - (.transform (indenting-transformer) source result))) + [e writer & opts] + (indent-xml (apply emit-str e opts) writer)) (defn indent-str "Emits the XML and indents the result. Writes the results to a String and returns it" - [e] - (let [^java.io.StringWriter sw (java.io.StringWriter.)] + [e & opts] + (let [sw (string-writer)] (indent e sw) - (.toString sw))) + (str sw))) diff --git a/src/main/clojure/clojure/data/xml/event.clj b/src/main/clojure/clojure/data/xml/event.clj index 9f01cdf..560d359 100644 --- a/src/main/clojure/clojure/data/xml/event.clj +++ b/src/main/clojure/clojure/data/xml/event.clj @@ -8,15 +8,81 @@ (ns clojure.data.xml.event "Data type for xml pull events" - {:author "Herwig Hochleitner"}) + {:author "Herwig Hochleitner"} + (:require [clojure.data.xml.protocols :refer + [EventGeneration gen-event next-events]] + #_[clojure.data.xml.name :refer [merge-nss separate-xmlns]] + [clojure.data.xml.node :refer [element* cdata xml-comment]] + [clojure.data.xml.impl :refer [extend-protocol-fns]]) + (:import (clojure.data.xml.node Element CData Comment))) ; Represents a parse event. ; type is one of :start-element, :end-element, or :characters -(defrecord Event [type name attrs str]) +(defrecord StartElementEvent [tag attrs nss]) +(defrecord EndElementEvent [tag]) +(defrecord CharsEvent [str]) +(defrecord CDataEvent [str]) +(defrecord CommentEvent [str]) -(defn event - ([type name] (Event. type name nil nil)) - ([type name attrs] (Event. type name attrs nil)) - ([type name attrs str] (Event. type name attrs str)) - ([type name attrs str meta] (Event. type name attrs str meta nil))) +;; Event Generation for stuff to show up in generated xml +(let [second-arg #(do %2)] + (extend-protocol-fns + EventGeneration + (StartElementEvent EndElementEvent CharsEvent CDataEvent CommentEvent) + {:gen-event identity + :next-events second-arg} + (String Boolean Number nil) + {:gen-event (comp ->CharsEvent str) + :next-events second-arg} + CData + {:gen-event (comp ->CDataEvent :content) + :next-events second-arg} + Comment + {:gen-event (comp ->CommentEvent :content) + :next-events second-arg})) + +(extend-protocol EventGeneration + Element + (gen-event [element] + #_(separate-xmlns #(->StartElementEvent + (:tag element) + %1 + (merge-nss (get (meta element) :clojure.data.xml/nss {}) + %2))) + ;; FIXME namespace awareness + (->StartElementEvent + (:tag element) + (:attrs element) + {}) + ;; /FIXME + ) + (next-events [element next-items] + (list* (:content element) + (->EndElementEvent (:tag element)) + next-items)) + + clojure.lang.Sequential + (gen-event [coll] + (gen-event (first coll))) + (next-events [coll next-items] + (if-let [r (seq (rest coll))] + (cons (next-events (first coll) r) next-items) + (next-events (first coll) next-items)))) + +;; Node Generation for events + +(defn event-element [event contents] + (when (instance? StartElementEvent event) + (element* (:tag event) (:attrs event) contents + {:clojure.data.xml/nss (:nss event)}))) + +(defn event-node [event] + (cond + (instance? CharsEvent event) (:str event) + (instance? CDataEvent event) (cdata (:str event)) + (instance? CommentEvent event) (xml-comment (:str event)) + :else (throw (ex-info "Illegal argument, not an event object" {:event event})))) + +(defn event-exit? [event] + (instance? EndElementEvent event)) diff --git a/src/main/clojure/clojure/data/xml/impl.clj b/src/main/clojure/clojure/data/xml/impl.clj index 8f16eb2..dbbe8cd 100644 --- a/src/main/clojure/clojure/data/xml/impl.clj +++ b/src/main/clojure/clojure/data/xml/impl.clj @@ -10,14 +10,42 @@ "Shared private code for data.xml namespaces" {:author "Herwig Hochleitner"}) +(defn- var-form? [form] + (and (seq? form) (= 'var (first form)))) + (defn- export-form [var-name] - (let [vsym (symbol (name var-name))] + (let [is-var (var-form? var-name) + vsym (symbol (name (if is-var (second var-name) var-name)))] `[(def ~vsym ~var-name) (alter-meta! (var ~vsym) - (constantly (assoc (meta (var ~var-name)) - :wrapped-by (var ~vsym))))])) + (constantly (assoc (meta ~(if is-var + var-name + `(var ~var-name))) + :wrapped-by (var ~vsym))))])) (defmacro export-api "This creates vars, that take their (local) name, value and metadata from another var" [& names] (cons 'do (mapcat export-form names))) + +(defmacro static-case + "Variant of case where keys are evaluated at compile-time" + [val & cases] + `(case ~val + ~@(mapcat (fn [[field thunk]] + [(eval field) thunk]) + (partition 2 cases)) + ~@(when (odd? (count cases)) + [(last cases)]))) + +(defmacro extend-protocol-fns + "Helper to many types to a protocol with a method map, similar to extend" + [proto & types+mmaps] + (assert (zero? (mod (count types+mmaps) 2))) + (let [gen-extend (fn [type mmap] (list `extend type proto mmap))] + `(do ~@(for [[type mmap] (partition 2 types+mmaps)] + (if (coll? type) + (let [mm (gensym "mm-")] + `(let [~mm ~mmap] + ~@(map gen-extend type (repeat mm)))) + (gen-extend type mmap)))))) diff --git a/src/main/clojure/clojure/data/xml/jvm/emit.clj b/src/main/clojure/clojure/data/xml/jvm/emit.clj new file mode 100644 index 0000000..8cbf0d7 --- /dev/null +++ b/src/main/clojure/clojure/data/xml/jvm/emit.clj @@ -0,0 +1,136 @@ +; Copyright (c) Rich Hickey. 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.jvm.emit + "JVM implementation of the emitter details" + {:author "Herwig Hochleitner"} + (:require (clojure.data.xml + #_[name :refer [qname-uri qname-local separate-xmlns]] + event) + [clojure.string :as str]) + (:import (java.io OutputStreamWriter Writer StringWriter) + (java.nio.charset Charset) + (javax.xml.namespace NamespaceContext) + (javax.xml.stream XMLStreamWriter XMLOutputFactory) + (javax.xml.transform OutputKeys Transformer + TransformerFactory) + (clojure.data.xml.event StartElementEvent EndElementEvent CharsEvent CDataEvent CommentEvent))) + +(defprotocol EventEmit + (emit-event [event ^XMLStreamWriter writer])) + +(defn check-stream-encoding [^OutputStreamWriter stream xml-encoding] + (when (not= (Charset/forName xml-encoding) (Charset/forName (.getEncoding stream))) + (throw (Exception. (str "Output encoding of stream (" xml-encoding + ") doesn't match declaration (" + (.getEncoding stream) ")"))))) + +;; properly namespace aware version +#_(defn- emit-attrs [^XMLStreamWriter writer attrs] + (doseq [[k v] attrs] + (let [uri (qname-uri k) + local (qname-local k)] + (if (str/blank? uri) + (.writeAttribute writer uri local (str v)) + (.writeAttribute writer local (str v)))))) + +;; The changes to the xmlns must be set before .writeStartElement +#_(defn- set-xmlns-attributes [^XMLStreamWriter writer ns-attrs] + ;; leave a list of thunks to write the attributes + (reduce (fn [left [k v]] + (let [local (qname-local k)] + (or (if (= "xmlns" local) + (when-not (= v (.. writer getNamespaceContext (getNamespaceURI ""))) + (.setDefaultNamespace writer v) + (conj left #(.writeDefaultNamespace writer v))) + (when-let [prefix (and (str/blank? (.getPrefix writer v)) + (if (.. writer getNamespaceContext + (getNamespaceURI local)) + ;; rename clashing prefixes + (str (gensym local)) + local))] + (.setPrefix writer prefix v) + (conj left #(.writeNamespace writer prefix v)))) + left))) + [] ns-attrs)) + +#_(defn- emit-start-tag [{:keys [attrs nss tag]} ^XMLStreamWriter writer] + (let [ns-attrs (set-xmlns-attributes writer nss) + uri (qname-uri tag) + local (qname-local tag)] + (separate-xmlns + attrs (fn [attrs xmlns] + (set-xmlns-attributes writer xmlns) + (.writeStartElement writer uri local) + (emit-attrs writer attrs))))) + +;; FIXME replace broken xmlns handling with code namespace aware version + +(defn qualified-name [event-name] + (if (instance? clojure.lang.Named event-name) + [(namespace event-name) (name event-name)] + (let [name-parts (str/split event-name #"/" 2)] + (if (= 2 (count name-parts)) + name-parts + [nil (first name-parts)])))) + +(defn write-attributes [attrs ^javax.xml.stream.XMLStreamWriter writer] + (doseq [[k v] attrs] + (let [[attr-ns attr-name] (qualified-name k)] + (if attr-ns + (.writeAttribute writer attr-ns attr-name (str v)) + (.writeAttribute writer attr-name (str v)))))) + +(defn emit-start-tag [event ^javax.xml.stream.XMLStreamWriter writer] + (let [[nspace qname] (qualified-name (:tag event))] + (.writeStartElement writer "" qname (or nspace "")) + (write-attributes (:attrs event) writer))) + +;; /FIXME + +(defn- emit-cdata [^String cdata-str ^XMLStreamWriter writer] + (when-not (str/blank? cdata-str) + (let [idx (.indexOf cdata-str "]]>")] + (if (= idx -1) + (.writeCData writer cdata-str ) + (do + (.writeCData writer (subs cdata-str 0 (+ idx 2))) + (recur (subs cdata-str (+ idx 2)) writer)))))) + +(extend-protocol EventEmit + StartElementEvent + (emit-event [ev writer] (emit-start-tag ev writer)) + EndElementEvent + (emit-event [ev writer] (.writeEndElement writer)) + CharsEvent + (emit-event [{:keys [str]} writer] (.writeCharacters writer str)) + CDataEvent + (emit-event [{:keys [str]} writer] (emit-cdata str writer)) + CommentEvent + (emit-event [{:keys [str]} writer] (.writeComment writer str))) + +;; Writers + +(defn write-document + "Writes the given event seq as XML text to writer. + Options: + :encoding Character encoding to use" + [^Writer swriter events opts] + (let [^XMLStreamWriter writer (-> (XMLOutputFactory/newInstance) + (.createXMLStreamWriter swriter))] + + (when (instance? OutputStreamWriter swriter) + (check-stream-encoding swriter (or (:encoding opts) "UTF-8"))) + + (.writeStartDocument writer (or (:encoding opts) "UTF-8") "1.0") + (doseq [event events] (emit-event event writer)) + (.writeEndDocument writer) + swriter)) + +(defn string-writer [] + (StringWriter.)) diff --git a/src/main/clojure/clojure/data/xml/jvm/parse.clj b/src/main/clojure/clojure/data/xml/jvm/parse.clj new file mode 100644 index 0000000..ac625a9 --- /dev/null +++ b/src/main/clojure/clojure/data/xml/jvm/parse.clj @@ -0,0 +1,98 @@ +; Copyright (c) Rich Hickey. 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.jvm.parse + (:require [clojure.string :as str] + [clojure.data.xml.event :refer + [->StartElementEvent ->EndElementEvent + ->CharsEvent ->CDataEvent ->CommentEvent]] + [clojure.data.xml.impl :refer + [static-case]]) + (:import + (javax.xml.stream + XMLInputFactory XMLStreamReader XMLStreamConstants))) + +(def ^{:private true} input-factory-props + {:allocator XMLInputFactory/ALLOCATOR + :coalescing XMLInputFactory/IS_COALESCING + :namespace-aware XMLInputFactory/IS_NAMESPACE_AWARE + :replacing-entity-references XMLInputFactory/IS_REPLACING_ENTITY_REFERENCES + :supporting-external-entities XMLInputFactory/IS_SUPPORTING_EXTERNAL_ENTITIES + :validating XMLInputFactory/IS_VALIDATING + :reporter XMLInputFactory/REPORTER + :resolver XMLInputFactory/RESOLVER + :support-dtd XMLInputFactory/SUPPORT_DTD}) + +(defn- attr-prefix [^XMLStreamReader sreader index] + (let [p (.getAttributePrefix sreader index)] + (when-not (str/blank? p) + p))) + +(defn- attr-hash [^XMLStreamReader sreader] (into {} + (for [i (range (.getAttributeCount sreader))] + [(keyword (attr-prefix sreader i) (.getAttributeLocalName sreader i)) + (.getAttributeValue sreader i)]))) + +; Note, sreader is mutable and mutated here in pull-seq, but it's +; protected by a lazy-seq so it's thread-safe. +(defn pull-seq + "Creates a seq of events. The XMLStreamConstants/SPACE clause below doesn't seem to + be triggered by the JDK StAX parser, but is by others. Leaving in to be more complete." + [^XMLStreamReader sreader include-node?] + (lazy-seq + (loop [] + (static-case + (.next sreader) + ; condp == (.next sreader) + XMLStreamConstants/START_ELEMENT + (if (include-node? :element) + (cons (->StartElementEvent (keyword (.getLocalName sreader)) + (attr-hash sreader) + (comment xmlns)) + (pull-seq sreader include-node?)) + (recur)) + XMLStreamConstants/END_ELEMENT + (if (include-node? :element) + (cons (->EndElementEvent (keyword (.getLocalName sreader))) + (pull-seq sreader include-node?)) + (recur)) + XMLStreamConstants/CHARACTERS + (if-let [text (and (include-node? :characters) + (not (.isWhiteSpace sreader)) + (.getText sreader))] + (cons (->CharsEvent text) + (pull-seq sreader include-node?)) + (recur)) + XMLStreamConstants/COMMENT + (if (include-node? :comment) + (cons (->CommentEvent (.getText sreader)) + (pull-seq sreader include-node?)) + (recur)) + XMLStreamConstants/END_DOCUMENT + nil + ;; Consume and ignore comments, spaces, processing instructions etc + (recur))))) + +(defn- make-input-factory [props] + (let [fac (XMLInputFactory/newInstance)] + (doseq [[k v] props + :when (contains? input-factory-props k) + :let [prop (input-factory-props k)]] + (.setProperty fac prop v)) + fac)) + +(defn make-stream-reader [props source] + (let [fac (make-input-factory props)] + ;; Reflection on following line cannot be eliminated via a + ;; type hint, because s is advertised by fn parse to be an + ;; InputStream or Reader, and there are different + ;; createXMLStreamReader signatures for each of those types. + (.createXMLStreamReader fac source))) + +(defn string-source [s] + (java.io.StringReader. s)) diff --git a/src/main/clojure/clojure/data/xml/jvm/pprint.clj b/src/main/clojure/clojure/data/xml/jvm/pprint.clj new file mode 100644 index 0000000..4d04210 --- /dev/null +++ b/src/main/clojure/clojure/data/xml/jvm/pprint.clj @@ -0,0 +1,27 @@ +; Copyright (c) Rich Hickey. 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.jvm.pprint + (:import + (javax.xml.transform Transformer OutputKeys TransformerFactory) + (java.io Writer StringReader StringWriter) + (javax.xml.transform.stream StreamSource StreamResult))) + +(defn ^Transformer indenting-transformer [] + (doto (-> (TransformerFactory/newInstance) .newTransformer) + (.setOutputProperty (OutputKeys/INDENT) "yes") + (.setOutputProperty (OutputKeys/METHOD) "xml") + (.setOutputProperty "{http://xml.apache.org/xslt}indent-amount" "2"))) + +(defn indent-xml + [xml-str ^Writer writer] + (let [source (-> xml-str StringReader. StreamSource.) + result (StreamResult. writer)] + (.transform (indenting-transformer) source result))) + + diff --git a/src/main/clojure/clojure/data/xml/protocols.clj b/src/main/clojure/clojure/data/xml/protocols.clj new file mode 100644 index 0000000..37677b2 --- /dev/null +++ b/src/main/clojure/clojure/data/xml/protocols.clj @@ -0,0 +1,26 @@ +; Copyright (c) Rich Hickey. 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.protocols) + +;; XML names can be any data type that has at least a namespace uri and a name slot + +(defprotocol AsQName + (qname-local [qname] "Get the name for this qname") + (qname-uri [qname] "Get the namespace uri for this qname")) + +(defprotocol EventGeneration + "Protocol for generating new events based on element type" + (gen-event [item] + "Function to generate an event for e.") + (next-events [item next-items] + "Returns the next set of events that should occur after e. next-events are the + events that should be generated after this one is complete.")) + +(defprotocol AsElements + (as-elements [expr] "Return a seq of elements represented by an expression.")) diff --git a/src/main/clojure/clojure/data/xml/prxml.clj b/src/main/clojure/clojure/data/xml/prxml.clj new file mode 100644 index 0000000..9fb0583 --- /dev/null +++ b/src/main/clojure/clojure/data/xml/prxml.clj @@ -0,0 +1,76 @@ +; Copyright (c) Rich Hickey. 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.prxml + (:require + [clojure.data.xml.protocols :refer [AsElements as-elements]] + [clojure.data.xml.node :refer [cdata xml-comment element* element]])) + +(defn sexp-element [tag attrs child] + (cond + (= :-cdata tag) (cdata (first child)) + (= :-comment tag) (xml-comment (first child)) + :else (element* tag attrs (mapcat as-elements child)))) + +(extend-protocol AsElements + clojure.lang.IPersistentVector + (as-elements [v] + (let [[tag & [attrs & after-attrs :as content]] v + [attrs content] (if (map? attrs) + [(into {} (for [[k v] attrs] + [k (str v)])) + after-attrs] + [{} content])] + [(sexp-element tag attrs content)])) + + clojure.lang.ISeq + (as-elements [s] + (mapcat as-elements s)) + + clojure.lang.Keyword + (as-elements [k] + [(element k)]) + + java.lang.String + (as-elements [s] + [s]) + + nil + (as-elements [_] nil) + + java.lang.Object + (as-elements [o] + [(str o)])) + +(defn sexps-as-fragment + "Convert a compact prxml/hiccup-style data structure into the more formal + tag/attrs/content format. A seq of elements will be returned, which may + not be suitable for immediate use as there is no root element. See also + sexp-as-element. + + The format is [:tag-name attr-map? content*]. Each vector opens a new tag; + seqs do not open new tags, and are just used for inserting groups of elements + into the parent tag. A bare keyword not in a vector creates an empty element. + + To provide XML conversion for your own data types, extend the AsElements + protocol to them." + ([] nil) + ([sexp] (as-elements sexp)) + ([sexp & sexps] (mapcat as-elements (cons sexp sexps)))) + +(defn sexp-as-element + "Convert a single sexp into an Element" + [sexp] + (let [[root & more] (sexps-as-fragment sexp)] + (when more + (throw + (IllegalArgumentException. + "Cannot have multiple root elements; try creating a fragment instead"))) + root)) + + diff --git a/src/main/clojure/clojure/data/xml/tree.clj b/src/main/clojure/clojure/data/xml/tree.clj new file mode 100644 index 0000000..3836744 --- /dev/null +++ b/src/main/clojure/clojure/data/xml/tree.clj @@ -0,0 +1,71 @@ +; Copyright (c) Rich Hickey. 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.tree + (:require [clojure.data.xml.protocols :refer + [gen-event next-events]] + [clojure.data.xml.event :refer + [event-element event-node event-exit?]])) + +(defn seq-tree + "Takes a seq of events that logically represents + a tree by each event being one of: enter-sub-tree event, + exit-sub-tree event, or node event. + + Returns a lazy sequence whose first element is a sequence of + sub-trees and whose remaining elements are events that are not + siblings or descendants of the initial event. + + The given exit? function must return true for any exit-sub-tree + event. parent must be a function of two arguments: the first is an + event, the second a sequence of nodes or subtrees that are children + of the event. parent must return nil or false if the event is not + an enter-sub-tree event. Any other return value will become + a sub-tree of the output tree and should normally contain in some + way the children passed as the second arg. The node function is + called with a single event arg on every event that is neither parent + nor exit, and its return value will become a node of the output tree. + + (seq-tree #(when (= %1 :<) (vector %2)) #{:>} str + [1 2 :< 3 :< 4 :> :> 5 :> 6]) + ;=> ((\"1\" \"2\" [(\"3\" [(\"4\")])] \"5\") 6)" + [parent exit? node coll] + (lazy-seq + (when-let [[event] (seq coll)] + (let [more (rest coll)] + (if (exit? event) + (cons nil more) + (let [tree (seq-tree parent exit? node more)] + (if-let [p (parent event (lazy-seq (first tree)))] + (let [subtree (seq-tree parent exit? node (lazy-seq (rest tree)))] + (cons (cons p (lazy-seq (first subtree))) + (lazy-seq (rest subtree)))) + (cons (cons (node event) (lazy-seq (first tree))) + (lazy-seq (rest tree)))))))))) + +;; # Break circular dependency of emitter-parser common infrastructure + +;; "Parse" events off the in-memory representation + +(defn flatten-elements + "Flatten a collection of elements to an event seq" + [elements] + (when (seq elements) + (lazy-seq + (let [e (first elements)] + (cons (gen-event e) + (flatten-elements (next-events e (rest elements)))))))) + +;; "Emit" events to the in-memory representation + +(defn event-tree + "Returns a lazy tree of Element objects for the given seq of Event + objects. See source-seq and parse." + [events] + (ffirst + (seq-tree event-element event-exit? event-node events))) diff --git a/src/test/clojure/clojure/data/xml/test_emit.clj b/src/test/clojure/clojure/data/xml/test_emit.clj index a75ed80..fa5dd3f 100644 --- a/src/test/clojure/clojure/data/xml/test_emit.clj +++ b/src/test/clojure/clojure/data/xml/test_emit.clj @@ -9,9 +9,10 @@ (ns ^{:doc "Tests for emit to print XML text." :author "Chris Houser"} clojure.data.xml.test-emit - (:use clojure.test - clojure.data.xml - [clojure.data.xml.test-utils :only (test-stream lazy-parse*)])) + (:require + [clojure.test :refer :all] + [clojure.data.xml :refer :all] + [clojure.data.xml.test-utils :refer [test-stream lazy-parse*]])) (def deep-tree (lazy-parse* (str "" diff --git a/src/test/clojure/clojure/data/xml/test_entities.clj b/src/test/clojure/clojure/data/xml/test_entities.clj index 42ae214..a6b8353 100644 --- a/src/test/clojure/clojure/data/xml/test_entities.clj +++ b/src/test/clojure/clojure/data/xml/test_entities.clj @@ -8,10 +8,10 @@ (ns ^{:doc "Test that external entities are not resolved by default, see https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Processing" :author "Carlo Sciolla"} - clojure.data.xml.test-entities - (:use clojure.test - clojure.data.xml) - (:require [clojure.java.io :as io])) + clojure.data.xml.test-entities + (:require [clojure.java.io :as io] + [clojure.test :refer :all] + [clojure.data.xml :refer :all])) (defn vulnerable-input "Creates an XML with an external entity referring to the given URL" diff --git a/src/test/clojure/clojure/data/xml/test_fresh.clj b/src/test/clojure/clojure/data/xml/test_fresh.clj new file mode 100644 index 0000000..ffd6a24 --- /dev/null +++ b/src/test/clojure/clojure/data/xml/test_fresh.clj @@ -0,0 +1,4 @@ +(ns clojure.data.xml.test-fresh + (:require + [clojure.data.xml :refer :all])) + diff --git a/src/test/clojure/clojure/data/xml/test_names.clj b/src/test/clojure/clojure/data/xml/test_names.clj new file mode 100644 index 0000000..9b1272d --- /dev/null +++ b/src/test/clojure/clojure/data/xml/test_names.clj @@ -0,0 +1,19 @@ +(ns clojure.data.xml.test-names + (:require [clojure.data.xml.name :refer :all] + [clojure.test :refer :all])) + +#_(defns xmlns.uri "uri:" + "P" "uri2:") + +#_(deftest test-types + (are [vals values] (every? #{vals} + (map (juxt qname-uri qname-local) + values)) + ["" "name"] ["name" :name] + ["uri:" "name"] ["{uri:}name" :xmlns.uri/name] + ["uri2:" "name2"] ["{uri2:}name2" :xmlns.uri/P:name2] + [xml-ns-uri "name"] [:xml/name] + [xmlns-ns-uri "name"] [:xmlns/name])) + + + diff --git a/src/test/clojure/clojure/data/xml/test_parse.clj b/src/test/clojure/clojure/data/xml/test_parse.clj index e5e5f83..65bc77b 100644 --- a/src/test/clojure/clojure/data/xml/test_parse.clj +++ b/src/test/clojure/clojure/data/xml/test_parse.clj @@ -9,9 +9,10 @@ (ns ^{:doc "Tests for XML parsing functions." :author "Chris Houser"} clojure.data.xml.test-parse - (:use clojure.test - clojure.data.xml - [clojure.data.xml.test-utils :only [test-stream lazy-parse*]])) + (:require + [clojure.test :refer :all] + [clojure.data.xml :refer [parse-str element]] + [clojure.data.xml.test-utils :refer [test-stream lazy-parse*]])) (deftest simple (let [input "This is bold test" @@ -78,4 +79,4 @@ (let [input ""] (is (= ["\nfoo bar\n\nbaz\n"] (:content (parse-str input)))) (is (= ["\nfoo bar\n" "\nbaz\n"] (:content - (parse-str input :coalescing false)))))) \ No newline at end of file + (parse-str input :coalescing false)))))) diff --git a/src/test/clojure/clojure/data/xml/test_pprint.clj b/src/test/clojure/clojure/data/xml/test_pprint.clj new file mode 100644 index 0000000..2557214 --- /dev/null +++ b/src/test/clojure/clojure/data/xml/test_pprint.clj @@ -0,0 +1,27 @@ +; Copyright (c) Rich Hickey. 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 ^{:doc "Tests for emit to print XML text." + :author "Herwig Hochleitner"} + clojure.data.xml.test-pprint + (:require + [clojure.test :refer :all] + [clojure.data.xml :refer :all])) + +(def xml + "") +(def indented-xml + ;; FIXME indent first + " + + +") + +(deftest test-indent + (is (= indented-xml (indent-str (parse-str xml))))) + diff --git a/src/test/clojure/clojure/data/xml/test_seq_tree.clj b/src/test/clojure/clojure/data/xml/test_seq_tree.clj index 44fac5d..9c0ed97 100644 --- a/src/test/clojure/clojure/data/xml/test_seq_tree.clj +++ b/src/test/clojure/clojure/data/xml/test_seq_tree.clj @@ -9,8 +9,8 @@ (ns ^{:doc "Tests for seq-tree, building a lazy tree from lazy seq." :author "Chris Houser"} clojure.data.xml.test-seq-tree - (:use clojure.test - clojure.data.xml) + (:require [clojure.test :refer :all] + [clojure.data.xml.tree :refer [seq-tree]]) (:import (java.lang.ref WeakReference))) (def tt diff --git a/src/test/clojure/clojure/data/xml/test_sexp.clj b/src/test/clojure/clojure/data/xml/test_sexp.clj index 0b89a00..b2a1e4d 100644 --- a/src/test/clojure/clojure/data/xml/test_sexp.clj +++ b/src/test/clojure/clojure/data/xml/test_sexp.clj @@ -9,9 +9,10 @@ (ns ^{:doc "Tests for reading [:tag {:attr 'value} body*] as XML." :author "Alan Malloy"} clojure.data.xml.test-sexp - (:use clojure.test - clojure.data.xml - [clojure.data.xml.test-utils :only (test-stream lazy-parse*)])) + (:require + [clojure.test :refer :all] + [clojure.data.xml :refer :all] + [clojure.data.xml.test-utils :refer (test-stream lazy-parse*)])) (deftest as-element (let [xml-input "" diff --git a/src/test/clojure/clojure/data/xml/test_utils.clj b/src/test/clojure/clojure/data/xml/test_utils.clj index 64052e4..0856203 100644 --- a/src/test/clojure/clojure/data/xml/test_utils.clj +++ b/src/test/clojure/clojure/data/xml/test_utils.clj @@ -9,11 +9,11 @@ (ns ^{:doc "Tests for emit to print XML text." :author "Chris Houser"} clojure.data.xml.test-utils - (:require [clojure.data.xml :as xml])) + (:require [clojure.data.xml :as xml :refer [parse]])) (defn test-stream [x] (java.io.ByteArrayInputStream. (.getBytes x "UTF-8"))) -(def lazy-parse* (comp xml/parse test-stream)) +(def lazy-parse* (comp parse test-stream)) From 3eef6919f56ef6e73760dda4a490c79a095fcef1 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Tue, 22 Sep 2015 12:01:00 +0200 Subject: [PATCH 062/237] machinery for qualified names - xml-name to clj ns mapping - QName parsing --- src/main/clojure/clojure/data/xml.clj | 5 +- .../clojure/clojure/data/xml/jvm/name.clj | 34 ++++ src/main/clojure/clojure/data/xml/name.clj | 147 ++++++++++++++++++ .../clojure/clojure/data/xml/test_names.clj | 34 ++-- 4 files changed, 205 insertions(+), 15 deletions(-) create mode 100644 src/main/clojure/clojure/data/xml/jvm/name.clj create mode 100644 src/main/clojure/clojure/data/xml/name.clj diff --git a/src/main/clojure/clojure/data/xml.clj b/src/main/clojure/clojure/data/xml.clj index 6fcb545..0d5a210 100644 --- a/src/main/clojure/clojure/data/xml.clj +++ b/src/main/clojure/clojure/data/xml.clj @@ -17,7 +17,7 @@ [impl :refer [export-api]] [node :as node] [prxml :as prxml] - #_[name :as name]) + [name :as name]) (clojure.data.xml.jvm [pprint :refer [indent-xml]] @@ -31,8 +31,7 @@ (export-api node/element* node/element node/cdata node/xml-comment node/to-element prxml/sexp-as-element prxml/sexps-as-fragment - ;name/parse-qname name/qname-uri name/qname-local name/to-qname name/ns-uri name/uri-ns name/declare-ns name/alias-ns - ) + name/parse-qname name/qname-uri name/qname-local name/to-qname name/ns-uri name/uri-ns name/declare-ns name/alias-ns) (defn event-seq "Parses the XML InputSource source using a pull-parser. Returns diff --git a/src/main/clojure/clojure/data/xml/jvm/name.clj b/src/main/clojure/clojure/data/xml/jvm/name.clj new file mode 100644 index 0000000..6eb3380 --- /dev/null +++ b/src/main/clojure/clojure/data/xml/jvm/name.clj @@ -0,0 +1,34 @@ +; Copyright (c) Rich Hickey. 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.jvm.name + (:require (clojure.data.xml + [protocols :refer [AsQName qname-uri qname-local]]) + [clojure.string :as str]) + (:import java.io.Writer + (javax.xml.namespace NamespaceContext QName))) + +(extend-protocol AsQName + QName + (qname-local [qname] (.getLocalPart qname)) + (qname-uri [qname] (.getNamespaceURI qname))) + +(def parse-qname + (memoize + (fn [s] + ;; TODO weakly memoize this? + (QName/valueOf s)))) + +(defn to-qname + ([o] + (cond (instance? QName o) o + (string? o) (parse-qname o) + (map? o) (to-qname (:uri o) (:local o) (:prefix o)) + :else (to-qname (qname-uri o) (qname-local o) ""))) + ([uri name prefix] + (QName. uri name prefix))) diff --git a/src/main/clojure/clojure/data/xml/name.clj b/src/main/clojure/clojure/data/xml/name.clj new file mode 100644 index 0000000..e5e346e --- /dev/null +++ b/src/main/clojure/clojure/data/xml/name.clj @@ -0,0 +1,147 @@ +; Copyright (c) Rich Hickey. 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.name + (:require [clojure.string :as str] + [clojure.data.xml.jvm.name :as jvm] + (clojure.data.xml + [impl :refer [export-api]] + [protocols :as protocols :refer [AsQName]])) + (:import (clojure.lang Namespace Keyword))) + +(export-api + jvm/parse-qname jvm/to-qname + ;; protocol functions can be redefined by extend-* + #'protocols/qname-uri #'protocols/qname-local) + +;; The empty string shall be equal to nil for xml names +(defn namespaced? [qn] + (not (str/blank? (qname-uri qn)))) + +(defn- clj-ns-name [ns] + (cond (instance? Namespace ns) (ns-name ns) + (keyword? ns) (name ns) + :else (str ns))) + +;; # Handling of xmlns - cljns bindings + +(def ^:private nss (atom {:ns->xs {} :xs->ns {}})) + +(defn ns-uri + "Look up xmlns uri to keyword namespace" + [ns] + (get-in @nss [:ns->xs (clj-ns-name ns)])) + +(defn uri-ns + "Look up keyword namespace to xmlns uri" + [uri] + (get-in @nss [:xs->ns uri])) + +(extend-protocol AsQName + clojure.lang.Keyword + (qname-local [kw] (name kw)) + (qname-uri [kw] + (if-let [ns (namespace kw)] + (or (ns-uri ns) + (throw (ex-info (str "Unknown xmlns for clj ns: " ns) + {:qname kw}))) + "")) + String + (qname-local [s] (qname-local (parse-qname s))) + (qname-uri [s] (qname-uri (parse-qname s)))) + +(defn- declare-ns* [{:keys [ns->xs xs->ns] :as acc} [ns xmlns & rst :as nss]] + (if (seq nss) + (do (assert (>= (count nss) 2)) + (let [n (clj-ns-name ns)] + (if-let [x' (ns->xs n)] + (if (= xmlns x') + (recur acc rst) + (throw (ex-info (str "Redefining " n) {:old x' :new xmlns}))) + (if-let [n' (xs->ns xmlns)] + (throw (ex-info (str xmlns " already bound to " n') + {:old n' :new n})) + (recur {:ns->xs (assoc ns->xs n xmlns) + :xs->ns (assoc xs->ns xmlns n)} + rst))))) + acc)) + +(defn declare-ns + "Define mappings in the global keyword-ns -> qname-uri mapping table. + Arguments are pairs of ns-name - qname-uri + ns-name must be a string, symbol, keyword or clojure namespace. The canonical form is string. + ns-uri must be a string" + {:arglists '([& {:as cljns-xmlnss}])} + [& ns-xmlnss] + (swap! nss declare-ns* ns-xmlnss)) + +(declare-ns + :xml "http://www.w3.org/XML/1998/namespace" + :xmlns "http://www.w3.org/2000/xmlns/" + :xml.dav "DAV:") + +(def ^:const empty-namespace + {"xml" (ns-uri :xml) + "xmlns" (ns-uri :xmlns)}) + +(def ^:const xmlns-uri (ns-uri :xmlns)) + +(defn alias-ns + "Define a clojure namespace alias for shortened keyword and symbol namespaces. + Similar to clojure.core/alias, but if namespace doesn't exist, it is created. + + ## Example + ;; (declare-ns :xml.dav \"DAV:\") ; already in stdlib + (alias-ns :D :xml.dav) + {:tag ::D/propfind :content []}" + {:arglists '([& {:as alias-nss}])} + [& ans] + (loop [[a n & rst :as ans] ans] + (when (seq ans) + (assert (<= 2 (count ans)) (pr-str ans)) + (let [ns (symbol (clj-ns-name n)) + al (symbol (clj-ns-name a))] + (create-ns ns) + (alias al ns) + (recur rst))))) + +(defn merge-nss + "Merge two attribute sets, deleting assignments of empty-string" + [nss1 nss2] + (persistent! + (reduce-kv (fn [a k v] + (if (str/blank? v) + (dissoc! a k) + (assoc! a k v))) + (transient nss1) + nss2))) + +(defn xmlns-attr? + "Is this qname an xmlns declaration?" + [qn] + (let [uri (qname-uri qn) + local (qname-local qn)] + (or (= xmlns-uri uri) + (and (str/blank? uri) + (= "xmlns" local))))) + +(defn separate-xmlns + "Call cont with two args: attributes and xmlns attributes" + [attrs cont] + (loop [attrs* (transient {}) + xmlns* (transient {}) + [[qn val] :as attrs'] attrs] + (if (seq attrs') + (if (xmlns-attr? qn) + (recur attrs* + (assoc! xmlns* qn val) + (next attrs')) + (recur (assoc! attrs* qn val) + xmlns* + (next attrs'))) + (cont (persistent! attrs*) (persistent! xmlns*))))) diff --git a/src/test/clojure/clojure/data/xml/test_names.clj b/src/test/clojure/clojure/data/xml/test_names.clj index 9b1272d..cf4722f 100644 --- a/src/test/clojure/clojure/data/xml/test_names.clj +++ b/src/test/clojure/clojure/data/xml/test_names.clj @@ -1,19 +1,29 @@ (ns clojure.data.xml.test-names - (:require [clojure.data.xml.name :refer :all] + (:require [clojure.data.xml :refer :all] [clojure.test :refer :all])) -#_(defns xmlns.uri "uri:" - "P" "uri2:") +(declare-ns + :test.xmlns.u "uri-u:" + "test.xmlns.v" "uri-v:" + 'test.xmlns.w "uri-w:" + *ns* "uri-t:") -#_(deftest test-types - (are [vals values] (every? #{vals} - (map (juxt qname-uri qname-local) - values)) - ["" "name"] ["name" :name] - ["uri:" "name"] ["{uri:}name" :xmlns.uri/name] - ["uri2:" "name2"] ["{uri2:}name2" :xmlns.uri/P:name2] - [xml-ns-uri "name"] [:xml/name] - [xmlns-ns-uri "name"] [:xmlns/name])) +(alias-ns + :U :test.xmlns.u + 'V 'test.xmlns.v + "W" "test.xmlns.w" + :T *ns*) + +(deftest test-types + (are [vals values] (every? true? (for [v values] + (is (= vals [(qname-uri v) (qname-local v)]) + (str "Interpreted QName: " (pr-str v))))) + ["" "name"] ["name" :name (parse-qname "name")] + ["uri-u:" "name"] [:test.xmlns.u/name ::U/name "{uri-u:}name" (parse-qname "{uri-u:}name")] + ["uri-v:" "vname"] [:test.xmlns.v/vname ::V/vname "{uri-v:}vname" (parse-qname "{uri-v:}vname")] + ["uri-w:" "wname"] [:test.xmlns.w/wname ::W/wname "{uri-w:}wname" (parse-qname "{uri-w:}wname")] + ["http://www.w3.org/XML/1998/namespace" "name"] [:xml/name] + ["http://www.w3.org/2000/xmlns/" "name"] [:xmlns/name])) From 49c8f6e2613c92e9346d54e2542c9700c44ef685 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Wed, 23 Sep 2015 03:51:29 +0200 Subject: [PATCH 063/237] Namespaced representations and roundtrip See http://dev.clojure.org/display/DXML/Namespaced+XML for design discussion. --- src/main/clojure/clojure/data/xml.clj | 15 ++- src/main/clojure/clojure/data/xml/event.clj | 43 ++++----- .../clojure/clojure/data/xml/jvm/emit.clj | 96 +++++++------------ .../clojure/clojure/data/xml/jvm/name.clj | 10 +- .../clojure/clojure/data/xml/jvm/parse.clj | 49 +++++++--- src/main/clojure/clojure/data/xml/name.clj | 40 +++++--- .../clojure/clojure/data/xml/test_emit.clj | 11 ++- .../clojure/clojure/data/xml/test_names.clj | 11 +++ 8 files changed, 148 insertions(+), 127 deletions(-) diff --git a/src/main/clojure/clojure/data/xml.clj b/src/main/clojure/clojure/data/xml.clj index 0d5a210..73f28b6 100644 --- a/src/main/clojure/clojure/data/xml.clj +++ b/src/main/clojure/clojure/data/xml.clj @@ -17,7 +17,8 @@ [impl :refer [export-api]] [node :as node] [prxml :as prxml] - [name :as name]) + [name :as name] + [event :as event]) (clojure.data.xml.jvm [pprint :refer [indent-xml]] @@ -29,9 +30,9 @@ [clojure.data.xml.tree :refer [event-tree flatten-elements]])) -(export-api node/element* node/element node/cdata node/xml-comment node/to-element - prxml/sexp-as-element prxml/sexps-as-fragment - name/parse-qname name/qname-uri name/qname-local name/to-qname name/ns-uri name/uri-ns name/declare-ns name/alias-ns) +(export-api node/element* node/element node/cdata node/xml-comment + prxml/sexp-as-element prxml/sexps-as-fragment event/element-nss + name/parse-qname name/qname-uri name/qname-local name/make-qname name/ns-uri name/uri-ns name/declare-ns name/alias-ns) (defn event-seq "Parses the XML InputSource source using a pull-parser. Returns @@ -46,7 +47,8 @@ :supporting-external-entities false} props)] (pull-seq (make-stream-reader props* source) - (get props* :include-node?)))) + (get props* :include-node?) + nil))) (defn parse "Parses the source, which can be an @@ -90,3 +92,6 @@ (let [sw (string-writer)] (indent e sw) (str sw))) + +;; TODO implement ~normalize to simulate an emit-parse roundtrip +;; in terms of xmlns environment and keywords vs qnames diff --git a/src/main/clojure/clojure/data/xml/event.clj b/src/main/clojure/clojure/data/xml/event.clj index 560d359..2eb9f99 100644 --- a/src/main/clojure/clojure/data/xml/event.clj +++ b/src/main/clojure/clojure/data/xml/event.clj @@ -11,11 +11,20 @@ {:author "Herwig Hochleitner"} (:require [clojure.data.xml.protocols :refer [EventGeneration gen-event next-events]] - #_[clojure.data.xml.name :refer [merge-nss separate-xmlns]] + [clojure.data.xml.name :refer [merge-nss separate-xmlns]] [clojure.data.xml.node :refer [element* cdata xml-comment]] [clojure.data.xml.impl :refer [extend-protocol-fns]]) (:import (clojure.data.xml.node Element CData Comment))) +(definline element-nss* [element] + (get (meta element) :clojure.data.xml/nss {})) + +(defn element-nss + "Get xmlns environment from element" + [{:keys [attrs] :as element}] + (separate-xmlns + attrs #(merge-nss (element-nss* element) %2))) + ; Represents a parse event. ; type is one of :start-element, :end-element, or :characters (defrecord StartElementEvent [tag attrs nss]) @@ -26,7 +35,14 @@ ;; Event Generation for stuff to show up in generated xml -(let [second-arg #(do %2)] +(let [second-arg #(do %2) + elem-event-generation + {:gen-event (fn elem-gen-event [{:keys [tag attrs] :as element}] + (separate-xmlns + attrs #(->StartElementEvent + tag %1 (merge-nss (element-nss* element) %2)))) + :next-events (fn elem-next-events [{:keys [tag content]} next-items] + (list* content (->EndElementEvent tag) next-items))}] (extend-protocol-fns EventGeneration (StartElementEvent EndElementEvent CharsEvent CDataEvent CommentEvent) @@ -40,28 +56,11 @@ :next-events second-arg} Comment {:gen-event (comp ->CommentEvent :content) - :next-events second-arg})) + :next-events second-arg} + (clojure.lang.IPersistentMap Element) elem-event-generation)) (extend-protocol EventGeneration - Element - (gen-event [element] - #_(separate-xmlns #(->StartElementEvent - (:tag element) - %1 - (merge-nss (get (meta element) :clojure.data.xml/nss {}) - %2))) - ;; FIXME namespace awareness - (->StartElementEvent - (:tag element) - (:attrs element) - {}) - ;; /FIXME - ) - (next-events [element next-items] - (list* (:content element) - (->EndElementEvent (:tag element)) - next-items)) - + clojure.lang.Sequential (gen-event [coll] (gen-event (first coll))) diff --git a/src/main/clojure/clojure/data/xml/jvm/emit.clj b/src/main/clojure/clojure/data/xml/jvm/emit.clj index 8cbf0d7..932e156 100644 --- a/src/main/clojure/clojure/data/xml/jvm/emit.clj +++ b/src/main/clojure/clojure/data/xml/jvm/emit.clj @@ -10,7 +10,7 @@ "JVM implementation of the emitter details" {:author "Herwig Hochleitner"} (:require (clojure.data.xml - #_[name :refer [qname-uri qname-local separate-xmlns]] + [name :refer [qname-uri qname-local separate-xmlns]] event) [clojure.string :as str]) (:import (java.io OutputStreamWriter Writer StringWriter) @@ -31,67 +31,43 @@ (.getEncoding stream) ")"))))) ;; properly namespace aware version -#_(defn- emit-attrs [^XMLStreamWriter writer attrs] - (doseq [[k v] attrs] - (let [uri (qname-uri k) - local (qname-local k)] - (if (str/blank? uri) - (.writeAttribute writer uri local (str v)) - (.writeAttribute writer local (str v)))))) - -;; The changes to the xmlns must be set before .writeStartElement -#_(defn- set-xmlns-attributes [^XMLStreamWriter writer ns-attrs] - ;; leave a list of thunks to write the attributes - (reduce (fn [left [k v]] - (let [local (qname-local k)] - (or (if (= "xmlns" local) - (when-not (= v (.. writer getNamespaceContext (getNamespaceURI ""))) - (.setDefaultNamespace writer v) - (conj left #(.writeDefaultNamespace writer v))) - (when-let [prefix (and (str/blank? (.getPrefix writer v)) - (if (.. writer getNamespaceContext - (getNamespaceURI local)) - ;; rename clashing prefixes - (str (gensym local)) - local))] - (.setPrefix writer prefix v) - (conj left #(.writeNamespace writer prefix v)))) - left))) - [] ns-attrs)) - -#_(defn- emit-start-tag [{:keys [attrs nss tag]} ^XMLStreamWriter writer] - (let [ns-attrs (set-xmlns-attributes writer nss) - uri (qname-uri tag) - local (qname-local tag)] - (separate-xmlns - attrs (fn [attrs xmlns] - (set-xmlns-attributes writer xmlns) - (.writeStartElement writer uri local) - (emit-attrs writer attrs))))) - -;; FIXME replace broken xmlns handling with code namespace aware version - -(defn qualified-name [event-name] - (if (instance? clojure.lang.Named event-name) - [(namespace event-name) (name event-name)] - (let [name-parts (str/split event-name #"/" 2)] - (if (= 2 (count name-parts)) - name-parts - [nil (first name-parts)])))) - -(defn write-attributes [attrs ^javax.xml.stream.XMLStreamWriter writer] +(defn- emit-attrs [^XMLStreamWriter writer attrs] (doseq [[k v] attrs] - (let [[attr-ns attr-name] (qualified-name k)] - (if attr-ns - (.writeAttribute writer attr-ns attr-name (str v)) - (.writeAttribute writer attr-name (str v)))))) + (let [uri (qname-uri k) + local (qname-local k)] + (if (str/blank? uri) + (.writeAttribute writer local (str v)) + (.writeAttribute writer uri local (str v)))))) -(defn emit-start-tag [event ^javax.xml.stream.XMLStreamWriter writer] - (let [[nspace qname] (qualified-name (:tag event))] - (.writeStartElement writer "" qname (or nspace "")) - (write-attributes (:attrs event) writer))) - -;; /FIXME +;; The changes to the xmlns must be set before .writeStartElement +(defn- set-xmlns-attributes [^XMLStreamWriter writer ns-attrs] + (let [thunks (reduce-kv (fn [left k v] + (let [local (qname-local k)] + (or (if (= "xmlns" local) + (when-not (= v (.. writer getNamespaceContext (getNamespaceURI ""))) + (.setDefaultNamespace writer v) + (cons #(.writeDefaultNamespace writer v) left)) + (when-let [prefix (and (str/blank? (.getPrefix writer v)) + (if (.. writer getNamespaceContext + (getNamespaceURI local)) + ;; rename clashing prefixes + (str (gensym local)) + local))] + (.setPrefix writer prefix v) + (cons #(.writeNamespace writer prefix v) left))) + left))) + nil ns-attrs)] + #(doseq [f thunks] (f)))) + +(defn- emit-start-tag [{:keys [attrs nss tag]} ^XMLStreamWriter writer] + (let [write-ns-attrs (set-xmlns-attributes writer nss) + uri (qname-uri tag) + local (qname-local tag)] + (if (str/blank? uri) + (.writeStartElement writer local) + (.writeStartElement writer uri local)) + (write-ns-attrs) + (emit-attrs writer attrs))) (defn- emit-cdata [^String cdata-str ^XMLStreamWriter writer] (when-not (str/blank? cdata-str) diff --git a/src/main/clojure/clojure/data/xml/jvm/name.clj b/src/main/clojure/clojure/data/xml/jvm/name.clj index 6eb3380..fdd007d 100644 --- a/src/main/clojure/clojure/data/xml/jvm/name.clj +++ b/src/main/clojure/clojure/data/xml/jvm/name.clj @@ -24,11 +24,5 @@ ;; TODO weakly memoize this? (QName/valueOf s)))) -(defn to-qname - ([o] - (cond (instance? QName o) o - (string? o) (parse-qname o) - (map? o) (to-qname (:uri o) (:local o) (:prefix o)) - :else (to-qname (qname-uri o) (qname-local o) ""))) - ([uri name prefix] - (QName. uri name prefix))) +(definline make-qname [uri name prefix] + `(QName. ~uri ~name ~prefix)) diff --git a/src/main/clojure/clojure/data/xml/jvm/parse.clj b/src/main/clojure/clojure/data/xml/jvm/parse.clj index ac625a9..4821974 100644 --- a/src/main/clojure/clojure/data/xml/jvm/parse.clj +++ b/src/main/clojure/clojure/data/xml/jvm/parse.clj @@ -12,7 +12,9 @@ [->StartElementEvent ->EndElementEvent ->CharsEvent ->CDataEvent ->CommentEvent]] [clojure.data.xml.impl :refer - [static-case]]) + [static-case]] + [clojure.data.xml.name :refer + [canonical-name]]) (:import (javax.xml.stream XMLInputFactory XMLStreamReader XMLStreamConstants))) @@ -33,17 +35,32 @@ (when-not (str/blank? p) p))) -(defn- attr-hash [^XMLStreamReader sreader] (into {} - (for [i (range (.getAttributeCount sreader))] - [(keyword (attr-prefix sreader i) (.getAttributeLocalName sreader i)) - (.getAttributeValue sreader i)]))) +(defn- attr-hash [^XMLStreamReader sreader] + (persistent! + (reduce (fn [tr i] + (assoc! tr (canonical-name (.getAttributeNamespace sreader i) + (.getAttributeLocalName sreader i) + (.getAttributePrefix sreader i)) + (.getAttributeValue sreader i))) + (transient {}) + (range (.getAttributeCount sreader))))) + +(defn- nss-hash [^XMLStreamReader sreader parent-hash] + (persistent! + (reduce (fn [tr i] + (let [ns-pf (.getNamespacePrefix sreader i)] + (assoc! tr (if (str/blank? ns-pf) + :xmlns ns-pf) + (.getNamespaceURI sreader i)))) + (transient parent-hash) + (range (.getNamespaceCount sreader))))) ; Note, sreader is mutable and mutated here in pull-seq, but it's ; protected by a lazy-seq so it's thread-safe. (defn pull-seq "Creates a seq of events. The XMLStreamConstants/SPACE clause below doesn't seem to be triggered by the JDK StAX parser, but is by others. Leaving in to be more complete." - [^XMLStreamReader sreader include-node?] + [^XMLStreamReader sreader include-node? ns-envs] (lazy-seq (loop [] (static-case @@ -51,27 +68,31 @@ ; condp == (.next sreader) XMLStreamConstants/START_ELEMENT (if (include-node? :element) - (cons (->StartElementEvent (keyword (.getLocalName sreader)) - (attr-hash sreader) - (comment xmlns)) - (pull-seq sreader include-node?)) + (let [ns-env (nss-hash sreader (or (first ns-envs) {}))] + (cons (->StartElementEvent (canonical-name (.getNamespaceURI sreader) + (.getLocalName sreader) + (.getPrefix sreader)) + (attr-hash sreader) + ns-env) + (pull-seq sreader include-node? (cons ns-env ns-envs)))) (recur)) XMLStreamConstants/END_ELEMENT (if (include-node? :element) - (cons (->EndElementEvent (keyword (.getLocalName sreader))) - (pull-seq sreader include-node?)) + (do (assert (seq ns-envs) "Balanced end") + (cons (->EndElementEvent (keyword (.getLocalName sreader))) + (pull-seq sreader include-node? (rest ns-envs)))) (recur)) XMLStreamConstants/CHARACTERS (if-let [text (and (include-node? :characters) (not (.isWhiteSpace sreader)) (.getText sreader))] (cons (->CharsEvent text) - (pull-seq sreader include-node?)) + (pull-seq sreader include-node? ns-envs)) (recur)) XMLStreamConstants/COMMENT (if (include-node? :comment) (cons (->CommentEvent (.getText sreader)) - (pull-seq sreader include-node?)) + (pull-seq sreader include-node? ns-envs)) (recur)) XMLStreamConstants/END_DOCUMENT nil diff --git a/src/main/clojure/clojure/data/xml/name.clj b/src/main/clojure/clojure/data/xml/name.clj index e5e346e..d884c17 100644 --- a/src/main/clojure/clojure/data/xml/name.clj +++ b/src/main/clojure/clojure/data/xml/name.clj @@ -15,7 +15,7 @@ (:import (clojure.lang Namespace Keyword))) (export-api - jvm/parse-qname jvm/to-qname + jvm/parse-qname jvm/make-qname ;; protocol functions can be redefined by extend-* #'protocols/qname-uri #'protocols/qname-local) @@ -35,12 +35,12 @@ (defn ns-uri "Look up xmlns uri to keyword namespace" [ns] - (get-in @nss [:ns->xs (clj-ns-name ns)])) + (-> @nss :ns->xs (get ns))) (defn uri-ns "Look up keyword namespace to xmlns uri" [uri] - (get-in @nss [:xs->ns uri])) + (-> @nss :xs->ns (get uri))) (extend-protocol AsQName clojure.lang.Keyword @@ -48,13 +48,22 @@ (qname-uri [kw] (if-let [ns (namespace kw)] (or (ns-uri ns) - (throw (ex-info (str "Unknown xmlns for clj ns: " ns) + (throw (ex-info (str "Unknown xmlns for clj ns: " (pr-str ns)) {:qname kw}))) "")) String (qname-local [s] (qname-local (parse-qname s))) (qname-uri [s] (qname-uri (parse-qname s)))) +(definline canonical-name [uri local prefix] + `(let [uri# ~uri local# ~local] + (if (str/blank? uri#) + (keyword local#) + (let [kw-prefix# (uri-ns uri#)] + (if (str/blank? kw-prefix#) + (make-qname uri# local# ~prefix) + (keyword kw-prefix# local#)))))) + (defn- declare-ns* [{:keys [ns->xs xs->ns] :as acc} [ns xmlns & rst :as nss]] (if (seq nss) (do (assert (>= (count nss) 2)) @@ -86,10 +95,10 @@ :xml.dav "DAV:") (def ^:const empty-namespace - {"xml" (ns-uri :xml) - "xmlns" (ns-uri :xmlns)}) + {"xml" (ns-uri "xml") + "xmlns" (ns-uri "xmlns")}) -(def ^:const xmlns-uri (ns-uri :xmlns)) +(def ^:const xmlns-uri (ns-uri "xmlns")) (defn alias-ns "Define a clojure namespace alias for shortened keyword and symbol namespaces. @@ -135,13 +144,14 @@ [attrs cont] (loop [attrs* (transient {}) xmlns* (transient {}) - [[qn val] :as attrs'] attrs] + [qn :as attrs'] (keys attrs)] (if (seq attrs') - (if (xmlns-attr? qn) - (recur attrs* - (assoc! xmlns* qn val) - (next attrs')) - (recur (assoc! attrs* qn val) - xmlns* - (next attrs'))) + (let [val (get attrs qn)] + (if (xmlns-attr? qn) + (recur attrs* + (assoc! xmlns* qn val) + (next attrs')) + (recur (assoc! attrs* qn val) + xmlns* + (next attrs')))) (cont (persistent! attrs*) (persistent! xmlns*))))) diff --git a/src/test/clojure/clojure/data/xml/test_emit.clj b/src/test/clojure/clojure/data/xml/test_emit.clj index fa5dd3f..0b14bc3 100644 --- a/src/test/clojure/clojure/data/xml/test_emit.clj +++ b/src/test/clojure/clojure/data/xml/test_emit.clj @@ -24,7 +24,7 @@ " t12t13t14" ""))) -(deftest defaults +(deftest test-defaults (testing "basic parsing" (let [expect (str "" "" @@ -38,8 +38,13 @@ (is (= expect (emit-str deep-tree))))) (testing "namespaced defaults" - (let [expect (str "done")] - (is (= expect (emit-str (element "foo/bar" {"foo/item" 1} [(element "foo/baz" {"foo/item" 2} "done")]))))))) + (let [expect (str "done")] + (is (= expect (emit-str + (element "{DAV:}bar" {"{DAV:}item" 1 :xmlns/D "DAV:"} + [(element "{DAV:}baz" {:xml.dav/item 2} "done")])))) + (is (= expect (emit-str + {:tag "{DAV:}bar" :attrs {"{DAV:}item" 1 :xmlns/D "DAV:"} + :content [{:tag "{DAV:}baz" :attrs {:xml.dav/item 2} :content "done"}]})))))) (deftest mixed-quotes (is (= (lazy-parse* diff --git a/src/test/clojure/clojure/data/xml/test_names.clj b/src/test/clojure/clojure/data/xml/test_names.clj index cf4722f..8fb229d 100644 --- a/src/test/clojure/clojure/data/xml/test_names.clj +++ b/src/test/clojure/clojure/data/xml/test_names.clj @@ -12,6 +12,7 @@ :U :test.xmlns.u 'V 'test.xmlns.v "W" "test.xmlns.w" + :D :xml.dav :T *ns*) (deftest test-types @@ -26,4 +27,14 @@ ["http://www.w3.org/2000/xmlns/" "name"] [:xmlns/name])) +(deftest test-emit-raw + (are [node result] (= (emit-str node) result) + {:tag ::D/limit :attrs {:xmlns/D "DAV:"} + :content [{:tag ::D/nresults :content ["100"]}]} + "100")) +(deftest test-parse-raw + (are [xml result] (= (parse-str xml) result) + "100" + (element ::D/limit {} + (element ::D/nresults nil "100")))) From 13e41150afd263d5bd0e8d9448b1038f9ea43bea Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Wed, 14 Oct 2015 01:41:32 +0200 Subject: [PATCH 064/237] make-qname accept nil prefix --- src/main/clojure/clojure/data/xml/jvm/name.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/clojure/data/xml/jvm/name.clj b/src/main/clojure/clojure/data/xml/jvm/name.clj index fdd007d..1b5249a 100644 --- a/src/main/clojure/clojure/data/xml/jvm/name.clj +++ b/src/main/clojure/clojure/data/xml/jvm/name.clj @@ -25,4 +25,4 @@ (QName/valueOf s)))) (definline make-qname [uri name prefix] - `(QName. ~uri ~name ~prefix)) + `(QName. ~uri ~name (or ~prefix ""))) From 525c8202527eceb06bc27384cd46f0b2806b7497 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Wed, 14 Oct 2015 01:43:12 +0200 Subject: [PATCH 065/237] remove dav default prefix --- src/main/clojure/clojure/data/xml/name.clj | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/clojure/clojure/data/xml/name.clj b/src/main/clojure/clojure/data/xml/name.clj index d884c17..fa63e28 100644 --- a/src/main/clojure/clojure/data/xml/name.clj +++ b/src/main/clojure/clojure/data/xml/name.clj @@ -91,8 +91,7 @@ (declare-ns :xml "http://www.w3.org/XML/1998/namespace" - :xmlns "http://www.w3.org/2000/xmlns/" - :xml.dav "DAV:") + :xmlns "http://www.w3.org/2000/xmlns/") (def ^:const empty-namespace {"xml" (ns-uri "xml") From bad55b519e36609335a9f4b6b0d4f8f3b8677bb1 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Wed, 14 Oct 2015 01:43:29 +0200 Subject: [PATCH 066/237] Add to-qname coercion function --- src/main/clojure/clojure/data/xml.clj | 2 +- src/main/clojure/clojure/data/xml/name.clj | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/clojure/data/xml.clj b/src/main/clojure/clojure/data/xml.clj index 73f28b6..68d2687 100644 --- a/src/main/clojure/clojure/data/xml.clj +++ b/src/main/clojure/clojure/data/xml.clj @@ -32,7 +32,7 @@ (export-api node/element* node/element node/cdata node/xml-comment prxml/sexp-as-element prxml/sexps-as-fragment event/element-nss - name/parse-qname name/qname-uri name/qname-local name/make-qname name/ns-uri name/uri-ns name/declare-ns name/alias-ns) + name/parse-qname name/qname-uri name/qname-local name/make-qname name/ns-uri name/uri-ns name/declare-ns name/alias-ns name/to-qname) (defn event-seq "Parses the XML InputSource source using a pull-parser. Returns diff --git a/src/main/clojure/clojure/data/xml/name.clj b/src/main/clojure/clojure/data/xml/name.clj index fa63e28..cb77410 100644 --- a/src/main/clojure/clojure/data/xml/name.clj +++ b/src/main/clojure/clojure/data/xml/name.clj @@ -64,6 +64,9 @@ (make-qname uri# local# ~prefix) (keyword kw-prefix# local#)))))) +(definline to-qname [n] + `(let [n# ~n] (make-qname (qname-uri n#) (qname-local n#) nil))) + (defn- declare-ns* [{:keys [ns->xs xs->ns] :as acc} [ns xmlns & rst :as nss]] (if (seq nss) (do (assert (>= (count nss) 2)) From a225b42d0daa193e636df1185d68425c226998c0 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Wed, 14 Oct 2015 01:43:49 +0200 Subject: [PATCH 067/237] Add .toString method for xml elements --- src/main/clojure/clojure/data/xml/node.clj | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/clojure/data/xml/node.clj b/src/main/clojure/clojure/data/xml/node.clj index d2a6e2a..b41794d 100644 --- a/src/main/clojure/clojure/data/xml/node.clj +++ b/src/main/clojure/clojure/data/xml/node.clj @@ -8,11 +8,22 @@ (ns clojure.data.xml.node "Data types for xml nodes: Element, CData and Comment" - {:author "Herwig Hochleitner"}) + {:author "Herwig Hochleitner"} + (:require [clojure.data.xml.name :refer [to-qname]])) ;; Parsed data format ;; Represents a node of an XML tree -(defrecord Element [tag attrs content]) +(defrecord Element [tag attrs content] + Object + (toString [_] + (let [qname (to-qname tag)] + (apply str (concat ["<" qname] + (mapcat (fn [[n a]] + [" " (to-qname n) "=" (pr-str a)]) + attrs) + (if (seq content) + (concat [">"] content ["<" qname ">"]) + ["/>"])))))) (defrecord CData [content]) (defrecord Comment [content]) From 5896a50e2f46b5231215b24d950350741ef912bc Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Mon, 26 Oct 2015 14:04:49 +0100 Subject: [PATCH 068/237] Fix element-nss --- src/main/clojure/clojure/data/xml/event.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/clojure/data/xml/event.clj b/src/main/clojure/clojure/data/xml/event.clj index 2eb9f99..9cca608 100644 --- a/src/main/clojure/clojure/data/xml/event.clj +++ b/src/main/clojure/clojure/data/xml/event.clj @@ -17,7 +17,7 @@ (:import (clojure.data.xml.node Element CData Comment))) (definline element-nss* [element] - (get (meta element) :clojure.data.xml/nss {})) + `(get (meta ~element) :clojure.data.xml/nss {})) (defn element-nss "Get xmlns environment from element" From 4c6e763ddad3c2ee3805bb055e89f50e98f9dea1 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Mon, 26 Oct 2015 14:05:34 +0100 Subject: [PATCH 069/237] Add qname constructor and fix NPE --- .../clojure/clojure/data/xml/jvm/name.clj | 7 +++- src/main/clojure/clojure/data/xml/name.clj | 33 +++++++++++++++++-- .../clojure/clojure/data/xml/test_names.clj | 3 ++ 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/clojure/data/xml/jvm/name.clj b/src/main/clojure/clojure/data/xml/jvm/name.clj index 1b5249a..1d9d6fc 100644 --- a/src/main/clojure/clojure/data/xml/jvm/name.clj +++ b/src/main/clojure/clojure/data/xml/jvm/name.clj @@ -25,4 +25,9 @@ (QName/valueOf s)))) (definline make-qname [uri name prefix] - `(QName. ~uri ~name (or ~prefix ""))) + `(QName. ~uri ~name ~prefix)) + +(defn qname + ([name] (make-qname "" name "")) + ([uri name] (make-qname (or uri "") name "")) + ([uri name prefix] (make-qname (or uri "") name (or prefix "")))) diff --git a/src/main/clojure/clojure/data/xml/name.clj b/src/main/clojure/clojure/data/xml/name.clj index cb77410..415b463 100644 --- a/src/main/clojure/clojure/data/xml/name.clj +++ b/src/main/clojure/clojure/data/xml/name.clj @@ -15,7 +15,7 @@ (:import (clojure.lang Namespace Keyword))) (export-api - jvm/parse-qname jvm/make-qname + jvm/parse-qname jvm/make-qname jvm/qname ;; protocol functions can be redefined by extend-* #'protocols/qname-uri #'protocols/qname-local) @@ -65,7 +65,7 @@ (keyword kw-prefix# local#)))))) (definline to-qname [n] - `(let [n# ~n] (make-qname (qname-uri n#) (qname-local n#) nil))) + `(let [n# ~n] (make-qname (or (qname-uri n#) "") (qname-local n#) ""))) (defn- declare-ns* [{:keys [ns->xs xs->ns] :as acc} [ns xmlns & rst :as nss]] (if (seq nss) @@ -157,3 +157,32 @@ xmlns* (next attrs')))) (cont (persistent! attrs*) (persistent! xmlns*))))) + +;(set! *warn-on-reflection* true) + +(def ^:private ^"[C" prefix-alphabet + (char-array + (map char + (range (int \a) (inc (int \z)))))) + +(def ^{:dynamic true + :doc "Thread local counter for a single document"} + *gen-prefix-counter*) + +(defn gen-prefix + "Generates an xml prefix. + Zero-arity can only be called, when *gen-prefix-counter* is bound and will increment it." + ([] (let [c *gen-prefix-counter*] + (set! *gen-prefix-counter* (inc c)) + (gen-prefix c))) + ([n] + (let [cnt (alength prefix-alphabet) + sb (StringBuilder.)] + (loop [n* n] + (let [ch (mod n* cnt) + n** (quot n* cnt)] + (.append sb (aget prefix-alphabet ch)) + (if (pos? n**) + (recur n**) + (str sb))))))) + diff --git a/src/test/clojure/clojure/data/xml/test_names.clj b/src/test/clojure/clojure/data/xml/test_names.clj index 8fb229d..1ae6c4c 100644 --- a/src/test/clojure/clojure/data/xml/test_names.clj +++ b/src/test/clojure/clojure/data/xml/test_names.clj @@ -38,3 +38,6 @@ "100" (element ::D/limit {} (element ::D/nresults nil "100")))) + +(deftest qnames + (is (= (qname "foo") (to-qname :foo)))) From 8f47a1113152a2ab7df950b710837560d974ee52 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Mon, 26 Oct 2015 14:05:54 +0100 Subject: [PATCH 070/237] Add canonical-name and toString method --- src/main/clojure/clojure/data/xml.clj | 10 +++++++++- src/main/clojure/clojure/data/xml/node.clj | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/clojure/data/xml.clj b/src/main/clojure/clojure/data/xml.clj index 68d2687..d86ac89 100644 --- a/src/main/clojure/clojure/data/xml.clj +++ b/src/main/clojure/clojure/data/xml.clj @@ -14,6 +14,7 @@ (:require (clojure.data.xml + [process :as process] [impl :refer [export-api]] [node :as node] [prxml :as prxml] @@ -32,7 +33,14 @@ (export-api node/element* node/element node/cdata node/xml-comment prxml/sexp-as-element prxml/sexps-as-fragment event/element-nss - name/parse-qname name/qname-uri name/qname-local name/make-qname name/ns-uri name/uri-ns name/declare-ns name/alias-ns name/to-qname) + name/ns-uri name/uri-ns name/declare-ns name/alias-ns + name/parse-qname name/qname-uri name/qname-local name/qname name/to-qname + process/find-xmlns process/aggregate-xmlns) + +(defn canonical-name + "Put (q)name into canonical form as per ns-env" + [n] + (name/canonical-name (qname-uri n) (qname-local n) "")) (defn event-seq "Parses the XML InputSource source using a pull-parser. Returns diff --git a/src/main/clojure/clojure/data/xml/node.clj b/src/main/clojure/clojure/data/xml/node.clj index b41794d..ce762aa 100644 --- a/src/main/clojure/clojure/data/xml/node.clj +++ b/src/main/clojure/clojure/data/xml/node.clj @@ -22,7 +22,7 @@ [" " (to-qname n) "=" (pr-str a)]) attrs) (if (seq content) - (concat [">"] content ["<" qname ">"]) + (concat [">"] content [""]) ["/>"])))))) (defrecord CData [content]) (defrecord Comment [content]) From 1f511c10270d68f6c999715de2bfc89c84bfb4fe Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Mon, 26 Oct 2015 14:07:12 +0100 Subject: [PATCH 071/237] Add aggregate-xmlns to eagerly collect xmlns into root --- src/main/clojure/clojure/data/xml/process.clj | 52 +++++++++++++++++++ .../clojure/clojure/data/xml/test_process.clj | 16 ++++++ 2 files changed, 68 insertions(+) create mode 100644 src/main/clojure/clojure/data/xml/process.clj create mode 100644 src/test/clojure/clojure/data/xml/test_process.clj diff --git a/src/main/clojure/clojure/data/xml/process.clj b/src/main/clojure/clojure/data/xml/process.clj new file mode 100644 index 0000000..16d12e3 --- /dev/null +++ b/src/main/clojure/clojure/data/xml/process.clj @@ -0,0 +1,52 @@ +(ns clojure.data.xml.process + (:require [clojure.data.xml.event :refer [element-nss] :as evt] + [clojure.data.xml.name :as name :refer [gen-prefix *gen-prefix-counter* qname-uri]] + [clojure.data.xml.node :refer [element] :as node] + [clojure.data.xml.tree :refer [flatten-elements] :as tree] + [clojure.string :as str])) + +(defn- reduce-tree + "Optimized reducer for in-order traversal of nodes, with reduce-like accumulator" + [f init xml] + (loop [result init + {:as tree [child & next-children :as children] :content} xml + [parent & next-parents :as parents] ()] + (if (seq children) + (recur (f result tree) + child + (concat next-children parents)) + (if (seq parents) + (recur (f result tree) + parent + next-parents) + (f result tree))))) + +(defn- qname-uri-xf [xf] + (fn [s el] + (if (map? el) + (reduce-kv + (fn [s attr _] (xf s (qname-uri attr))) + (xf s (qname-uri (:tag el))) (:attrs el)) + s))) + +(defn find-xmlns + "Find all xmlns occuring in a root" + [xml] + (persistent! + (reduce-tree (qname-uri-xf conj!) + (transient #{}) xml))) + +(defn aggregate-xmlns + "Put all occurring xmlns into the root" + [xml] + (with-meta + xml {:clojure.data.xml/nss + (binding [*gen-prefix-counter* 0] + (persistent! + (reduce (fn [tm uri] + (if (str/blank? uri) + tm + (assoc! tm (keyword "xmlns" (gen-prefix)) uri))) + (transient {}) (find-xmlns xml))))})) + + diff --git a/src/test/clojure/clojure/data/xml/test_process.clj b/src/test/clojure/clojure/data/xml/test_process.clj new file mode 100644 index 0000000..6dde39a --- /dev/null +++ b/src/test/clojure/clojure/data/xml/test_process.clj @@ -0,0 +1,16 @@ +(ns clojure.data.xml.test-process + (:require [clojure.data.xml :refer :all] + [clojure.test :refer :all])) + +(def test-data + (element + :foo nil + (with-meta (element :bar {:xmlns "MOO:"} "some" "content") + {:clojure.data.xml/nss {:xmlns/p "PAR:"}}) + "more content" + (element (qname "GOO:" "ho") {(qname "GEE:" "hi") "ma"} "ii") + "end")) + +(deftest process + (is (= (find-xmlns test-data) #{"" "GEE:" "GOO:"})) + (is (= (element-nss (aggregate-xmlns test-data)) {:xmlns/a "GEE:" :xmlns/b "GOO:"}))) From f800bbf4acb1d8698ae2734d771c194e55a9a11f Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Thu, 5 Nov 2015 09:38:12 +0100 Subject: [PATCH 072/237] Fix missing xmlns in test suite --- src/test/clojure/clojure/data/xml/test_names.clj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/clojure/clojure/data/xml/test_names.clj b/src/test/clojure/clojure/data/xml/test_names.clj index 1ae6c4c..3f677b9 100644 --- a/src/test/clojure/clojure/data/xml/test_names.clj +++ b/src/test/clojure/clojure/data/xml/test_names.clj @@ -3,6 +3,7 @@ [clojure.test :refer :all])) (declare-ns + :xml.dav "DAV:" :test.xmlns.u "uri-u:" "test.xmlns.v" "uri-v:" 'test.xmlns.w "uri-w:" From 41aaffb316a67379b23561f242fb068eda9bb4cc Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Thu, 5 Nov 2015 09:43:02 +0100 Subject: [PATCH 073/237] Update comment to reflect removed std prefix --- src/main/clojure/clojure/data/xml/name.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/clojure/data/xml/name.clj b/src/main/clojure/clojure/data/xml/name.clj index 415b463..ab8593b 100644 --- a/src/main/clojure/clojure/data/xml/name.clj +++ b/src/main/clojure/clojure/data/xml/name.clj @@ -107,7 +107,7 @@ Similar to clojure.core/alias, but if namespace doesn't exist, it is created. ## Example - ;; (declare-ns :xml.dav \"DAV:\") ; already in stdlib + (declare-ns :xml.dav \"DAV:\") (alias-ns :D :xml.dav) {:tag ::D/propfind :content []}" {:arglists '([& {:as alias-nss}])} From 5f5500e8aeccb8b9f9f6219364fcbcdabc1ac70a Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Mon, 7 Dec 2015 15:12:33 +0100 Subject: [PATCH 074/237] reflect non-deterministic ordering of aggregate-xmlns in test --- src/test/clojure/clojure/data/xml/test_process.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/clojure/clojure/data/xml/test_process.clj b/src/test/clojure/clojure/data/xml/test_process.clj index 6dde39a..bef4522 100644 --- a/src/test/clojure/clojure/data/xml/test_process.clj +++ b/src/test/clojure/clojure/data/xml/test_process.clj @@ -13,4 +13,4 @@ (deftest process (is (= (find-xmlns test-data) #{"" "GEE:" "GOO:"})) - (is (= (element-nss (aggregate-xmlns test-data)) {:xmlns/a "GEE:" :xmlns/b "GOO:"}))) + (is (= (set (vals (element-nss (aggregate-xmlns test-data)))) #{"GEE:" "GOO:"}))) From 3eb5b2ee936dbe027d9ded9e5f92115e781deb02 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Mon, 7 Dec 2015 16:32:26 +0100 Subject: [PATCH 075/237] add Herwig Hochleitner to developers --- pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pom.xml b/pom.xml index 1895259..ed5b6d7 100644 --- a/pom.xml +++ b/pom.xml @@ -22,6 +22,11 @@ senior.ryan@gmail.com -6 + + Herwig Hochleitner + herwig@bendlas.net + +1 + From 8efaea8137f8784d95759c3a42a0494e9783e233 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Mon, 7 Dec 2015 16:33:14 +0100 Subject: [PATCH 076/237] add project.clj for development with leiningen --- project.clj | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 project.clj diff --git a/project.clj b/project.clj new file mode 100644 index 0000000..96b7f11 --- /dev/null +++ b/project.clj @@ -0,0 +1,5 @@ +(defproject org.clojure/data.xml "0-DEVELOPMENT" + :source-paths ["src/main/clojure"] + :test-paths ["src/test/clojure"] + :resource-paths ["src/test/resources"] + :dependencies [[org.clojure/clojure "1.8.0-RC3"]]) From 8b620747d032d71ae94ecae488aa4647ca54e687 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Tue, 15 Dec 2015 12:09:20 +0100 Subject: [PATCH 077/237] Auto - assign prefixes for unbound uris --- .../clojure/clojure/data/xml/jvm/emit.clj | 48 ++++++++++++------- .../clojure/clojure/data/xml/test_names.clj | 5 ++ 2 files changed, 37 insertions(+), 16 deletions(-) diff --git a/src/main/clojure/clojure/data/xml/jvm/emit.clj b/src/main/clojure/clojure/data/xml/jvm/emit.clj index 932e156..2490f6b 100644 --- a/src/main/clojure/clojure/data/xml/jvm/emit.clj +++ b/src/main/clojure/clojure/data/xml/jvm/emit.clj @@ -10,7 +10,7 @@ "JVM implementation of the emitter details" {:author "Herwig Hochleitner"} (:require (clojure.data.xml - [name :refer [qname-uri qname-local separate-xmlns]] + [name :refer [qname-uri qname-local separate-xmlns gen-prefix *gen-prefix-counter*]] event) [clojure.string :as str]) (:import (java.io OutputStreamWriter Writer StringWriter) @@ -39,8 +39,13 @@ (.writeAttribute writer local (str v)) (.writeAttribute writer uri local (str v)))))) +(defn- make-prefix [^NamespaceContext nc] + (let [pf (gen-prefix)] + (if (str/blank? (.getNamespaceURI nc pf)) + pf (recur nc)))) + ;; The changes to the xmlns must be set before .writeStartElement -(defn- set-xmlns-attributes [^XMLStreamWriter writer ns-attrs] +(defn- set-xmlns-attributes [^XMLStreamWriter writer ns-attrs used-uris] (let [thunks (reduce-kv (fn [left k v] (let [local (qname-local k)] (or (if (= "xmlns" local) @@ -51,18 +56,28 @@ (if (.. writer getNamespaceContext (getNamespaceURI local)) ;; rename clashing prefixes - (str (gensym local)) + (make-prefix (.getNamespaceContext writer)) local))] (.setPrefix writer prefix v) (cons #(.writeNamespace writer prefix v) left))) left))) - nil ns-attrs)] - #(doseq [f thunks] (f)))) + nil ns-attrs) + ;; Check that all uris used in the tag have a prefix + thunks' (reduce (fn [left uri] + (if (and (not (str/blank? uri)) + (str/blank? (.. writer getNamespaceContext + (getPrefix uri)))) + (let [prefix (make-prefix (.getNamespaceContext writer))] + (.setPrefix writer prefix uri) + (cons #(.writeNamespace writer prefix uri) left)) + left)) + thunks used-uris)] + #(doseq [f thunks'] (f)))) (defn- emit-start-tag [{:keys [attrs nss tag]} ^XMLStreamWriter writer] - (let [write-ns-attrs (set-xmlns-attributes writer nss) - uri (qname-uri tag) - local (qname-local tag)] + (let [uri (qname-uri tag) + local (qname-local tag) + write-ns-attrs (set-xmlns-attributes writer nss (cons uri (map qname-uri (keys attrs))))] (if (str/blank? uri) (.writeStartElement writer local) (.writeStartElement writer uri local)) @@ -97,16 +112,17 @@ Options: :encoding Character encoding to use" [^Writer swriter events opts] - (let [^XMLStreamWriter writer (-> (XMLOutputFactory/newInstance) - (.createXMLStreamWriter swriter))] + (binding [*gen-prefix-counter* 0] + (let [^XMLStreamWriter writer (-> (XMLOutputFactory/newInstance) + (.createXMLStreamWriter swriter))] - (when (instance? OutputStreamWriter swriter) - (check-stream-encoding swriter (or (:encoding opts) "UTF-8"))) + (when (instance? OutputStreamWriter swriter) + (check-stream-encoding swriter (or (:encoding opts) "UTF-8"))) - (.writeStartDocument writer (or (:encoding opts) "UTF-8") "1.0") - (doseq [event events] (emit-event event writer)) - (.writeEndDocument writer) - swriter)) + (.writeStartDocument writer (or (:encoding opts) "UTF-8") "1.0") + (doseq [event events] (emit-event event writer)) + (.writeEndDocument writer) + swriter))) (defn string-writer [] (StringWriter.)) diff --git a/src/test/clojure/clojure/data/xml/test_names.clj b/src/test/clojure/clojure/data/xml/test_names.clj index 3f677b9..0f1a606 100644 --- a/src/test/clojure/clojure/data/xml/test_names.clj +++ b/src/test/clojure/clojure/data/xml/test_names.clj @@ -42,3 +42,8 @@ (deftest qnames (is (= (qname "foo") (to-qname :foo)))) + +(deftest test-gen-prefix + (are [node] (= (parse-str (emit-str node)) node) + (element ::D/limit {::V/moo "gee"} + (element ::D/nresults nil "100")))) From 4086011888b99883e754ba4892ef2d4d664a40f2 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Tue, 15 Dec 2015 12:13:22 +0100 Subject: [PATCH 078/237] Add test for reassigning of prefixes --- src/test/clojure/clojure/data/xml/test_names.clj | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/test/clojure/clojure/data/xml/test_names.clj b/src/test/clojure/clojure/data/xml/test_names.clj index 0f1a606..11b26dd 100644 --- a/src/test/clojure/clojure/data/xml/test_names.clj +++ b/src/test/clojure/clojure/data/xml/test_names.clj @@ -47,3 +47,11 @@ (are [node] (= (parse-str (emit-str node)) node) (element ::D/limit {::V/moo "gee"} (element ::D/nresults nil "100")))) + +(deftest test-reassign-prefix + (are [node reparsed] (= (parse-str (emit-str node)) reparsed) + (element ::D/limit {:xmlns/D "DAV:"} + ;; because of outer binding, "uri-v:" will be bound to + ;; generated xmlns:a instead of xmlns:D + (element ::V/other {:xmlns/D "uri-v:"})) + (element ::D/limit {} (element ::V/other)))) From ddb3903fbb18805ff2f59d8edee0db72edf058cc Mon Sep 17 00:00:00 2001 From: Ryan Senior Date: Mon, 14 Dec 2015 21:22:00 -0600 Subject: [PATCH 079/237] Remove unused test file --- src/test/clojure/clojure/data/xml/test_fresh.clj | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 src/test/clojure/clojure/data/xml/test_fresh.clj diff --git a/src/test/clojure/clojure/data/xml/test_fresh.clj b/src/test/clojure/clojure/data/xml/test_fresh.clj deleted file mode 100644 index ffd6a24..0000000 --- a/src/test/clojure/clojure/data/xml/test_fresh.clj +++ /dev/null @@ -1,4 +0,0 @@ -(ns clojure.data.xml.test-fresh - (:require - [clojure.data.xml :refer :all])) - From 31558df7826fd388255bfe4c3b77ad4fbd8b89d1 Mon Sep 17 00:00:00 2001 From: Ryan Senior Date: Mon, 14 Dec 2015 21:22:10 -0600 Subject: [PATCH 080/237] Add docs in the README for the new namespaces feature --- README.md | 139 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 128 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index a7a5360..d9a9925 100644 --- a/README.md +++ b/README.md @@ -13,8 +13,8 @@ data.xml has the following features: ## JDK 1.5 -This library uses the pull parser that ships with JDK 1.6. If you running on JDK 1.6+, you do not need any -additional dependencies. If you are using JDK 1.5, you will need to include a dependency on StAX. More +This library uses the pull parser that ships with JDK 1.6. If you running on JDK 1.6+, you do not need any +additional dependencies. If you are using JDK 1.5, you will need to include a dependency on StAX. More information on this is available [here](https://github.com/clojure/data.xml/blob/jdk16-pull-parser/jdk_15_readme.txt) ## Bugs @@ -61,19 +61,19 @@ or #clojure.data.xml.Element{:tag :foo, :attrs {}, - :content (#clojure.data.xml.Element{:tag :bar, + :content (#clojure.data.xml.Element{:tag :bar, :attrs {}, :content (#clojure.data.xml.Element{:tag :baz, :attrs {}, :content ("The baz value")})})} The data is returned as defrecords and can be manipulated using the -normal clojure data structure functions. Additional parsing options +normal clojure data structure functions. Additional parsing options can be passed via key pairs: (parse-str "" :coalescing false) #clojure.data.xml.Element{:tag :a, :attrs {}, :content ("\nfoo bar\n" "\nbaz\n")} - + XML elements can be created using the typical defrecord constructor functions or the element function used below, and written using a [java.io.Writer](http://docs.oracle.com/javase/6/docs/api/java/io/Writer.html).: @@ -102,7 +102,7 @@ Comments and CDATA can also be emitted as an S-expression with the special tag n (= (element :tag {:attr "value"} (element :body {} (cdata "not parsed true + ;;-> true XML can be "round tripped" through the library: @@ -124,8 +124,8 @@ debugging. (element :baz {} "The baz value")))] (= tags (parse-str (emit-str tags)))) - true - + true + Indentation is supported, but should be treated as a debugging feature as it's likely to be pretty slow: @@ -174,10 +174,129 @@ But are ignored when read: (xml-comment "Just a goes here") (element :bar {} "and another element"))))) - "and another element" + "and another element" Generated API docs for data.xml are available [here](http://clojure.github.com/data.xml). +## Namespace Support + +Parsing and emitting XML namespaces are supported and use JDK built-in +classes. Below is an example of parsing an XHTML document: + + (parse-str " + + + Example title + + + Example Paragraph + + ") + + #...Element{:tag #object[javax.xml.namespace.QName 0x68651690 "{http://www.w3.org/1999/xhtml}html"], + :attrs {}, + :content (...)} + +The above data structures are verbose. Each tag that includes a +namespace will include that in it's QName: + + #...Element{:tag #object[javax.xml.namespace.QName 0x7255cde4 "{http://www.w3.org/1999/xhtml}title"], + :attrs {}, + :content ("Example title")} + +This is the most basic representation of the parsed document that +includes namespaces. Emitting namespace information in a similar way +can use the `qname` function: + + (element (qname "title" "http://www.w3.org/1999/xhtml" "foo") + {} + "Example title") + + #...Element{:tag #object[javax.xml.namespace.QName 0x22a22c0e "{title}http://www.w3.org/1999/xhtml"], + :attrs {}, + :content ("Example title")} + +The emitting code above is similarly verbose. By declaring the +namespaces that will be parsed or emitted up-front via `delcare-ns`, +these representations can be made much more succinct: + + (declare-ns "xh" "http://www.w3.org/1999/xhtml") + (parse-str " + + + Example title + + + Example Paragraph + + ") + + #...Element{:tag :xh/html, :attrs {}, :content (...)} + +In the above example, all tags use the namespace +`http://www.w3.org/1999/xhtml`. That namespace is declared as "xh" in +Clojure. All the tags parsed from that document will be +`:xh/the-tag-name`. Note that `xh` is not related to the namespace +prefix declared in the document (`foo` in this example). `xh` is just +an easy way to refer to the `http://www.w3.org/1999/xhtml` namespace +in code. + +The declared namespace can also be used when constructing XML +documents: + + (emit-str (element :xh/title + {:xmlns/foo "http://www.w3.org/1999/xhtml"} + "Example title")) + + "Example title" + +### Namespace Prefixes + +Explicitly declaring namespace prefixes in the code will result in a +user specified prefix: + + (emit-str (element (qname "http://www.w3.org/1999/xhtml" "title" "foo") + {:xmlns/foo "http://www.w3.org/1999/xhtml"} + "Example title")) + "Example title" + +Not specifying a namespace prefix will results in a prefix being generated: + + ;; Assumes (declare-ns "xh" "http://www.w3.org/1999/xhtml") + (emit-str (element :xh/title + {} + "Example title")) + + "Example title" + +The above example auto assigns prefixes for the namespaces used. In +this case it was named `a` by the emitter. Emitting several nested +tags with the same namespace will use one prefix: + + (emit-str (element :xh/html + {} + (element :xh/head + {} + (element :xh/title + {} + "Example title")))) + + "Example title" + +Note that the Java QName does not consider namespace prefixes when +checking equality. Similarly constructing QNames from string +representations does not preserve prefixes. Prefixes are treated +similarly in data.xml. Prefixes are currently represented as metadata +on the elements. This preserves the same equality behavior that QNames +have: + + (= (parse-str "Example title") + (parse-str "Example title")) + +Removing the metadata will cause the elements to not have a prefix, +which is still correct, but will cause new prefixes to be generated +when the document is emitted. + ## License Licensed under the [Eclipse Public License](http://www.opensource.org/licenses/eclipse-1.0.php). @@ -198,5 +317,3 @@ All contributions need to be made via patches attached to tickets in [JIRA](http://dev.clojure.org/jira/browse/DXML). Check the [Contributing to Clojure](http://clojure.org/contributing) page for more information. - - From b72a24ada30ba1b9d3f7d4253093c2a4640fb853 Mon Sep 17 00:00:00 2001 From: Ryan Senior Date: Wed, 16 Dec 2015 23:32:08 -0600 Subject: [PATCH 081/237] Bump the version to 0.1.0-SNAPSHOT --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ed5b6d7..d77dd01 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ 4.0.0 data.xml - 0.0.9-SNAPSHOT + 0.1.0-SNAPSHOT data.xml jar Functions to parse XML into lazy sequences and lazy trees and emit these as text From a2186e4da9fb8320fff570967bd2e8928d5592e7 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Thu, 17 Dec 2015 13:30:31 +0100 Subject: [PATCH 082/237] README: clarify on declare-ns vs alias-ns --- README.md | 52 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index d9a9925..f47c7ac 100644 --- a/README.md +++ b/README.md @@ -180,8 +180,8 @@ Generated API docs for data.xml are available [here](http://clojure.github.com/d ## Namespace Support -Parsing and emitting XML namespaces are supported and use JDK built-in -classes. Below is an example of parsing an XHTML document: +Parsing and emitting XML namespaces are supported and use the JDK built-in +QName class. Below is an example of parsing an XHTML document: (parse-str " @@ -217,10 +217,10 @@ can use the `qname` function: :content ("Example title")} The emitting code above is similarly verbose. By declaring the -namespaces that will be parsed or emitted up-front via `delcare-ns`, +namespaces that will be parsed or emitted up-front via `declare-ns`, these representations can be made much more succinct: - (declare-ns "xh" "http://www.w3.org/1999/xhtml") + (declare-ns "xml.html" "http://www.w3.org/1999/xhtml") (parse-str " @@ -231,39 +231,49 @@ these representations can be made much more succinct: ") - #...Element{:tag :xh/html, :attrs {}, :content (...)} + #...Element{:tag :xml.html/html, :attrs {}, :content (...)} In the above example, all tags use the namespace -`http://www.w3.org/1999/xhtml`. That namespace is declared as "xh" in +`http://www.w3.org/1999/xhtml`. That namespace is declared as "xml.html" in Clojure. All the tags parsed from that document will be -`:xh/the-tag-name`. Note that `xh` is not related to the namespace -prefix declared in the document (`foo` in this example). `xh` is just -an easy way to refer to the `http://www.w3.org/1999/xhtml` namespace -in code. +`:xml.html/the-tag-name`. Note that `xml.html` is not related to the namespace +prefix declared in the document (`foo` in this example). `xml.html` is just +a way to refer to names in the `http://www.w3.org/1999/xhtml` namespace +with a keyword. -The declared namespace can also be used when constructing XML -documents: +The declared namespace can also be used in combination with the +regular clojure namespace aliasing mechnism. When constructing XML +documents, this leads pretty succinct representation with alias-aware keywords. - (emit-str (element :xh/title + (alias-ns :xh :xml.html) ;; alias-ns will create the target ns - xml.html - so that it can be aliased into the current ns + (emit-str (element ::xh/title {:xmlns/foo "http://www.w3.org/1999/xhtml"} "Example title")) "Example title" +Take note, that the keyword-namespaces `:xmlns/...` as well as +`:xml/...` are predefined to refer to `http://www.w3.org/2000/xmlns/` +and `http://www.w3.org/XML/1998/namespace` respectively. + +Because keywords interact with clojure's namespace - aliasing +mechanism, applications can choose descriptive names in `declare-ns`. + ### Namespace Prefixes -Explicitly declaring namespace prefixes in the code will result in a -user specified prefix: +Prefixes mostly an artifact of xml serialisation. They can be +customized, by explicitly declaring them as attributes in the `xmlns` +kw-namespace: - (emit-str (element (qname "http://www.w3.org/1999/xhtml" "title" "foo") + (emit-str (element (qname "http://www.w3.org/1999/xhtml" "title") {:xmlns/foo "http://www.w3.org/1999/xhtml"} "Example title")) "Example title" Not specifying a namespace prefix will results in a prefix being generated: - ;; Assumes (declare-ns "xh" "http://www.w3.org/1999/xhtml") - (emit-str (element :xh/title + ;; Assumes (declare-ns "xml.html" "http://www.w3.org/1999/xhtml") and (alias-ns :xh :xml.html) + (emit-str (element ::xh/title {} "Example title")) @@ -273,11 +283,11 @@ The above example auto assigns prefixes for the namespaces used. In this case it was named `a` by the emitter. Emitting several nested tags with the same namespace will use one prefix: - (emit-str (element :xh/html + (emit-str (element ::xh/html {} - (element :xh/head + (element ::xh/head {} - (element :xh/title + (element ::xh/title {} "Example title")))) From fef8ea1c04619a87b53544429025bd944bd225b7 Mon Sep 17 00:00:00 2001 From: Ryan Senior Date: Sat, 19 Dec 2015 07:10:02 -0600 Subject: [PATCH 083/237] Minor README fixup --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f47c7ac..44af327 100644 --- a/README.md +++ b/README.md @@ -261,8 +261,8 @@ mechanism, applications can choose descriptive names in `declare-ns`. ### Namespace Prefixes -Prefixes mostly an artifact of xml serialisation. They can be -customized, by explicitly declaring them as attributes in the `xmlns` +Prefixes are mostly an artifact of xml serialisation. They can be +customized by explicitly declaring them as attributes in the `xmlns` kw-namespace: (emit-str (element (qname "http://www.w3.org/1999/xhtml" "title") From 3e9d4d71dae82b8fa7fc0c29949b901f464d1c99 Mon Sep 17 00:00:00 2001 From: Ryan Senior Date: Sat, 19 Dec 2015 06:39:45 -0600 Subject: [PATCH 084/237] (DXML-28) Add test for emitting event-tree directly --- src/test/clojure/clojure/data/xml/test_emit.clj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/test/clojure/clojure/data/xml/test_emit.clj b/src/test/clojure/clojure/data/xml/test_emit.clj index 0b14bc3..2f9e1cc 100644 --- a/src/test/clojure/clojure/data/xml/test_emit.clj +++ b/src/test/clojure/clojure/data/xml/test_emit.clj @@ -146,3 +146,7 @@ (emit-str (element :foo {} (int 0))))) (is (= "1.2" (emit-str (element :foo {} (float 1.2)))))) + +(deftest test-event-seq-emit + (is (= "123" + (emit-str (event-seq (java.io.StringReader. "123") {}))))) From d03c42b46484de212f2f8d3b3a3cc6350f59b4ef Mon Sep 17 00:00:00 2001 From: Ryan Senior Date: Sat, 19 Dec 2015 09:38:24 -0600 Subject: [PATCH 085/237] Docs prep for 0.1.0-beta1 release --- CHANGES.md | 12 +++++++++++- README.md | 28 ++++++++++++++++++++++------ jdk_15_readme.txt | 22 ---------------------- 3 files changed, 33 insertions(+), 29 deletions(-) delete mode 100644 jdk_15_readme.txt diff --git a/CHANGES.md b/CHANGES.md index a11243a..4641e1f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,15 @@ -From 0.0.6 to 0.0.7 +From 0.0.8 to 0.1.0-beta1 +- Add support for XML namespaces (DXML-4) +- Fix pull-seq so it produces character events that work with emit-events (DXML-28) +- Removed docs and references to JDK 1.5, data.xml now requires 1.6+ + +From 0.0.7 to 0.0.8 +- Remove relection warnings in emit-cdata (DXML-16) +- Added an EPL license file (DXML-19) +- Fixed bug in the handling of CData end tags (DXML-17) +- Added support for emitting booleans and numbers (DXML-14) +From 0.0.6 to 0.0.7 - Fixed bug with args to the indentation function (DXML-7) - Strings now supported as tag names, previously was only kewords (DXML-8) - Add CDATA and comments support to sexp-as-element (DXML-11) diff --git a/README.md b/README.md index 44af327..6d4acba 100644 --- a/README.md +++ b/README.md @@ -11,12 +11,6 @@ data.xml has the following features: * Uses StAX internally * lazy - should allow parsing and emitting of large XML documents -## JDK 1.5 - -This library uses the pull parser that ships with JDK 1.6. If you running on JDK 1.6+, you do not need any -additional dependencies. If you are using JDK 1.5, you will need to include a dependency on StAX. More -information on this is available [here](https://github.com/clojure/data.xml/blob/jdk16-pull-parser/jdk_15_readme.txt) - ## Bugs Please report bugs using JIRA [here](http://dev.clojure.org/jira/browse/DXML). @@ -43,6 +37,28 @@ Add the following to the `project.clj` dependencies: [org.clojure/data.xml "0.0.8"] +## Installation - Beta + +Latest beta release: 0.1.0-beta1 + +* [All Released Versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22data.xml%22) + +* [Development Snapshot Versions](https://oss.sonatype.org/index.html#nexus-search;gav~org.clojure~data.xml~~~) + +### Maven +For Maven projects, add the following XML in your `pom.xml`'s `` section: + + + org.clojure + data.xml + 0.1.0-beta1 + + +### Leiningen +Add the following to the `project.clj` dependencies: + + [org.clojure/data.xml "0.1.0-beta1"] + ## Examples The examples below assume you have added a `use` for data.xml: diff --git a/jdk_15_readme.txt b/jdk_15_readme.txt deleted file mode 100644 index 9c10b4b..0000000 --- a/jdk_15_readme.txt +++ /dev/null @@ -1,22 +0,0 @@ -data.xml uses the StAX API for parsing XML documents. Projects on JDK -1.6+ will not need any additional dependencies. If you are on JDK 1.5, you -will need a StAX implementation. Below is the XML needed to add that -dependency to your maven POM: - - -... - - stax - stax - 1.2.0 - -... - - -And the Leiningen equivalent: -:dependencies [.. - [stax "1.2.0"] - ...] - -Note that other StAX implementations should work, but the above will -be better tested (and known to work). From 0531bf974be61d911401022c075a3a5ebc13083e Mon Sep 17 00:00:00 2001 From: Ryan Senior Date: Tue, 22 Dec 2015 15:59:24 -0600 Subject: [PATCH 086/237] Update version for 0.1.0-beta1 release --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d77dd01..c70068a 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ 4.0.0 data.xml - 0.1.0-SNAPSHOT + 0.1.0-beta1 data.xml jar Functions to parse XML into lazy sequences and lazy trees and emit these as text From bd813b40b8208f4d67105665b3a53178c1cc6385 Mon Sep 17 00:00:00 2001 From: Ryan Senior Date: Tue, 22 Dec 2015 16:03:44 -0600 Subject: [PATCH 087/237] Revert version bump --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c70068a..d77dd01 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ 4.0.0 data.xml - 0.1.0-beta1 + 0.1.0-SNAPSHOT data.xml jar Functions to parse XML into lazy sequences and lazy trees and emit these as text From d8b597fe29ce9ac9f30f94f9bd1ff9853e0cfeb3 Mon Sep 17 00:00:00 2001 From: Ryan Senior Date: Tue, 22 Dec 2015 16:27:51 -0600 Subject: [PATCH 088/237] Fix indentation on a test for JDK 6 and 7 JDK 8 includes a newline after the XML declaration, JDK 6 and 7 do not which is causing the build to fail on 6 and 7. --- src/test/clojure/clojure/data/xml/test_pprint.clj | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/test/clojure/clojure/data/xml/test_pprint.clj b/src/test/clojure/clojure/data/xml/test_pprint.clj index 2557214..b70a35d 100644 --- a/src/test/clojure/clojure/data/xml/test_pprint.clj +++ b/src/test/clojure/clojure/data/xml/test_pprint.clj @@ -15,12 +15,19 @@ (def xml "") + +(defn jdk8? [] + (-> (System/getProperty "java.version") + (.startsWith "1.8"))) + (def indented-xml - ;; FIXME indent first - " + (str + "" + (when-not (jdk8?) "\n") + " -") +")) (deftest test-indent (is (= indented-xml (indent-str (parse-str xml))))) From 72c651b12c53635d62292f26c5c62321b91842cf Mon Sep 17 00:00:00 2001 From: Ryan Senior Date: Tue, 22 Dec 2015 22:04:32 -0600 Subject: [PATCH 089/237] Updated CHANGES.md to note the Clojure 1.4.0+ requirement --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index 4641e1f..afc9167 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,7 @@ From 0.0.8 to 0.1.0-beta1 - Add support for XML namespaces (DXML-4) - Fix pull-seq so it produces character events that work with emit-events (DXML-28) - Removed docs and references to JDK 1.5, data.xml now requires 1.6+ +- data.xml now requires Clojure 1.4.0+ From 0.0.7 to 0.0.8 - Remove relection warnings in emit-cdata (DXML-16) From 9a7b726ea4b2affe97944fd626f0580ddcc140dd Mon Sep 17 00:00:00 2001 From: "Hudson @ build.clojure.org" Date: Wed, 23 Dec 2015 09:39:16 -0600 Subject: [PATCH 090/237] [maven-release-plugin] prepare release data.xml-0.1.0-beta1 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index d77dd01..9b37523 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ 4.0.0 data.xml - 0.1.0-SNAPSHOT + 0.1.0-beta1 data.xml jar Functions to parse XML into lazy sequences and lazy trees and emit these as text @@ -39,7 +39,7 @@ scm:git:git@github.com:clojure/data.xml.git scm:git:git@github.com:clojure/data.xml.git git@github.com:clojure/data.xml.git - HEAD + data.xml-0.1.0-beta1 From c541be3517ecd5d0761847ac6bd585c92c5d34ed Mon Sep 17 00:00:00 2001 From: "Hudson @ build.clojure.org" Date: Wed, 23 Dec 2015 09:39:17 -0600 Subject: [PATCH 091/237] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 9b37523..d77dd01 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ 4.0.0 data.xml - 0.1.0-beta1 + 0.1.0-SNAPSHOT data.xml jar Functions to parse XML into lazy sequences and lazy trees and emit these as text @@ -39,7 +39,7 @@ scm:git:git@github.com:clojure/data.xml.git scm:git:git@github.com:clojure/data.xml.git git@github.com:clojure/data.xml.git - data.xml-0.1.0-beta1 + HEAD From 9d8546f36061533a63329bbcaa9724e5fe7c79e6 Mon Sep 17 00:00:00 2001 From: Christian Egli Date: Mon, 4 Jul 2016 15:58:27 +0200 Subject: [PATCH 092/237] DXML-10 Add support for doctype declarations when emitting XML This fixes http://dev.clojure.org/jira/browse/DXML-10 The patch adds a :doctype option to write-document that allows to specify the doctype. Since all of emit, emit-str, indent and indent-str are based on write-document they all support the :doctype option now --- src/main/clojure/clojure/data/xml.clj | 10 +++++-- .../clojure/clojure/data/xml/jvm/emit.clj | 5 +++- .../clojure/clojure/data/xml/test_emit.clj | 28 +++++++++++++++++++ 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/clojure/data/xml.clj b/src/main/clojure/clojure/data/xml.clj index d86ac89..87220e6 100644 --- a/src/main/clojure/clojure/data/xml.clj +++ b/src/main/clojure/clojure/data/xml.clj @@ -76,12 +76,16 @@ (defn emit "Prints the given Element tree as XML text to stream. Options: - :encoding Character encoding to use" + :encoding Character encoding to use + :doctype Document type (DOCTYPE) declaration to use" [e writer & {:as opts}] (write-document writer (flatten-elements [e]) opts)) (defn emit-str - "Emits the Element to String and returns it" + "Emits the Element to String and returns it. + Options: + :encoding Character encoding to use + :doctype Document type (DOCTYPE) declaration to use" ([e & opts] (let [sw (string-writer)] (apply emit e sw opts) @@ -98,7 +102,7 @@ "Emits the XML and indents the result. Writes the results to a String and returns it" [e & opts] (let [sw (string-writer)] - (indent e sw) + (apply indent e sw opts) (str sw))) ;; TODO implement ~normalize to simulate an emit-parse roundtrip diff --git a/src/main/clojure/clojure/data/xml/jvm/emit.clj b/src/main/clojure/clojure/data/xml/jvm/emit.clj index 2490f6b..ae2f983 100644 --- a/src/main/clojure/clojure/data/xml/jvm/emit.clj +++ b/src/main/clojure/clojure/data/xml/jvm/emit.clj @@ -110,7 +110,8 @@ (defn write-document "Writes the given event seq as XML text to writer. Options: - :encoding Character encoding to use" + :encoding Character encoding to use + :doctype Document type (DOCTYPE) declaration to use" [^Writer swriter events opts] (binding [*gen-prefix-counter* 0] (let [^XMLStreamWriter writer (-> (XMLOutputFactory/newInstance) @@ -120,6 +121,8 @@ (check-stream-encoding swriter (or (:encoding opts) "UTF-8"))) (.writeStartDocument writer (or (:encoding opts) "UTF-8") "1.0") + (when-let [doctype (:doctype opts)] + (.writeDTD writer doctype)) (doseq [event events] (emit-event event writer)) (.writeEndDocument writer) swriter))) diff --git a/src/test/clojure/clojure/data/xml/test_emit.clj b/src/test/clojure/clojure/data/xml/test_emit.clj index 2f9e1cc..807552b 100644 --- a/src/test/clojure/clojure/data/xml/test_emit.clj +++ b/src/test/clojure/clojure/data/xml/test_emit.clj @@ -82,6 +82,27 @@ (binding [*out* (java.io.OutputStreamWriter. stream "UTF-8")] (emit (element :foo) *out* :encoding "ISO-8859-1")))))) +(deftest doctype + (let [input-tree + (lazy-parse* "cool") + doctype-html "" + doctype-html-401-transitional + "" + doctype-xhtml-10-strict + ""] + (is (= (str "" + doctype-html + "cool") + (emit-str input-tree :doctype doctype-html))) + (is (= (str "" + doctype-html-401-transitional + "cool") + (emit-str input-tree :doctype doctype-html-401-transitional))) + (is (= (str "" + doctype-xhtml-10-strict + "cool") + (emit-str input-tree :doctype doctype-xhtml-10-strict))))) + (deftest emitting-cdata (testing "basic cdata" (is (= (str "" @@ -133,6 +154,13 @@ result (indent-str nested-xml)] (is (= expect (subs result (.indexOf result "")))))) +(deftest test-indent-str-with-doctype + (let [nested-xml (lazy-parse* (str "foo")) + doctype "" + expect (str doctype "\n\n \n \n foo\n \n \n\n") + result (indent-str nested-xml :doctype doctype)] + (is (= expect (subs result (.indexOf result doctype)))))) + (deftest test-boolean (is (= "true" (emit-str (element :foo {} true))))) From 814825f4f7240a33d208f9a2aa604a79a43968e4 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Thu, 4 Aug 2016 21:15:20 +0200 Subject: [PATCH 093/237] DXML-33: fix emit of generated prefixes --- .../clojure/clojure/data/xml/jvm/emit.clj | 71 ++++++++++--------- .../clojure/clojure/data/xml/test_emit.clj | 6 ++ 2 files changed, 45 insertions(+), 32 deletions(-) diff --git a/src/main/clojure/clojure/data/xml/jvm/emit.clj b/src/main/clojure/clojure/data/xml/jvm/emit.clj index ae2f983..feeb74f 100644 --- a/src/main/clojure/clojure/data/xml/jvm/emit.clj +++ b/src/main/clojure/clojure/data/xml/jvm/emit.clj @@ -44,44 +44,51 @@ (if (str/blank? (.getNamespaceURI nc pf)) pf (recur nc)))) -;; The changes to the xmlns must be set before .writeStartElement -(defn- set-xmlns-attributes [^XMLStreamWriter writer ns-attrs used-uris] - (let [thunks (reduce-kv (fn [left k v] - (let [local (qname-local k)] - (or (if (= "xmlns" local) - (when-not (= v (.. writer getNamespaceContext (getNamespaceURI ""))) - (.setDefaultNamespace writer v) - (cons #(.writeDefaultNamespace writer v) left)) - (when-let [prefix (and (str/blank? (.getPrefix writer v)) - (if (.. writer getNamespaceContext - (getNamespaceURI local)) - ;; rename clashing prefixes - (make-prefix (.getNamespaceContext writer)) - local))] - (.setPrefix writer prefix v) - (cons #(.writeNamespace writer prefix v) left))) - left))) - nil ns-attrs) - ;; Check that all uris used in the tag have a prefix - thunks' (reduce (fn [left uri] - (if (and (not (str/blank? uri)) - (str/blank? (.. writer getNamespaceContext - (getPrefix uri)))) - (let [prefix (make-prefix (.getNamespaceContext writer))] - (.setPrefix writer prefix uri) - (cons #(.writeNamespace writer prefix uri) left)) - left)) - thunks used-uris)] - #(doseq [f thunks'] (f)))) +(defn- write-xmlns-attribute [^XMLStreamWriter writer k v] + (if (str/blank? k) + (do (.setDefaultNamespace writer v) + (.writeDefaultNamespace writer v)) + (do (.setPrefix writer v k) + (.writeNamespace writer v k))) + writer) + +(defn- get-prefix [^XMLStreamWriter writer temp-xmlns uri] + (or (get temp-xmlns uri) + (.getPrefix writer uri))) + +(defn- xmlns-attribute-set [^XMLStreamWriter writer ns-attrs used-uris] + (let [tleft (transient {}) + tleft (reduce-kv (fn [tleft k v] + (let [local (qname-local k)] + (or (if (= "xmlns" local) + (when-not (= v (.. writer getNamespaceContext (getNamespaceURI ""))) + (assoc! tleft v "")) + (when-let [prefix (and (str/blank? (get-prefix writer tleft v)) + (if (.. writer getNamespaceContext + (getNamespaceURI local)) + ;; rename clashing prefixes + (make-prefix (.getNamespaceContext writer)) + local))] + (assoc! tleft v prefix))) + tleft))) + tleft ns-attrs)] + (persistent! + (reduce (fn [tleft uri] + (if (and (not (str/blank? uri)) + (str/blank? (get-prefix writer tleft uri))) + (assoc! tleft uri (make-prefix (.getNamespaceContext writer))) + tleft)) + tleft used-uris)))) (defn- emit-start-tag [{:keys [attrs nss tag]} ^XMLStreamWriter writer] (let [uri (qname-uri tag) local (qname-local tag) - write-ns-attrs (set-xmlns-attributes writer nss (cons uri (map qname-uri (keys attrs))))] + xmlns-attrs (xmlns-attribute-set writer nss (cons uri (map qname-uri (keys attrs))))] (if (str/blank? uri) (.writeStartElement writer local) - (.writeStartElement writer uri local)) - (write-ns-attrs) + (.writeStartElement writer (get-prefix writer xmlns-attrs uri) + local uri)) + (reduce-kv write-xmlns-attribute writer xmlns-attrs) (emit-attrs writer attrs))) (defn- emit-cdata [^String cdata-str ^XMLStreamWriter writer] diff --git a/src/test/clojure/clojure/data/xml/test_emit.clj b/src/test/clojure/clojure/data/xml/test_emit.clj index 807552b..ce5aa0f 100644 --- a/src/test/clojure/clojure/data/xml/test_emit.clj +++ b/src/test/clojure/clojure/data/xml/test_emit.clj @@ -178,3 +178,9 @@ (deftest test-event-seq-emit (is (= "123" (emit-str (event-seq (java.io.StringReader. "123") {}))))) + +(deftest test-sibling-xmlns + (let [el (element (to-qname "{NS1}top") {} + (element (to-qname "{NS2}foo")) + (element (to-qname "{NS2}bar")))] + (is (= (parse-str (emit-str el)) el)))) From e84990d37ee25e6c7c4651bb0c435e8b77e17128 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Thu, 4 Aug 2016 21:29:25 +0200 Subject: [PATCH 094/237] DXML-30: fix emit of default namespaces --- src/main/clojure/clojure/data/xml/jvm/emit.clj | 2 +- src/test/clojure/clojure/data/xml/test_emit.clj | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/clojure/data/xml/jvm/emit.clj b/src/main/clojure/clojure/data/xml/jvm/emit.clj index feeb74f..72642fc 100644 --- a/src/main/clojure/clojure/data/xml/jvm/emit.clj +++ b/src/main/clojure/clojure/data/xml/jvm/emit.clj @@ -75,7 +75,7 @@ (persistent! (reduce (fn [tleft uri] (if (and (not (str/blank? uri)) - (str/blank? (get-prefix writer tleft uri))) + (nil? (get-prefix writer tleft uri))) (assoc! tleft uri (make-prefix (.getNamespaceContext writer))) tleft)) tleft used-uris)))) diff --git a/src/test/clojure/clojure/data/xml/test_emit.clj b/src/test/clojure/clojure/data/xml/test_emit.clj index ce5aa0f..10fb658 100644 --- a/src/test/clojure/clojure/data/xml/test_emit.clj +++ b/src/test/clojure/clojure/data/xml/test_emit.clj @@ -184,3 +184,8 @@ (element (to-qname "{NS2}foo")) (element (to-qname "{NS2}bar")))] (is (= (parse-str (emit-str el)) el)))) + +(deftest test-default-xmlns + (is (= {:clojure.data.xml/nss {:xmlns "NS"}} + (meta (parse-str "")) + (meta (parse-str (emit-str (parse-str ""))))))) From c3b7a94870038e35f3564c0d54faa59521c4e5e7 Mon Sep 17 00:00:00 2001 From: Ryan Senior Date: Sun, 28 Aug 2016 14:30:21 -0500 Subject: [PATCH 095/237] Update release notes for 0.1.0-beta2 --- CHANGES.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index afc9167..77ffa80 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,8 @@ +From 0.1.0-beta1 to 0.1.0-beta2 +- Add support for emitting DOCTYPEs (DXML-10) +- Fix issue emitting sibling namespaces (DXML-33) +- Fix issue printing defaulted namespaces (DXML-30) + From 0.0.8 to 0.1.0-beta1 - Add support for XML namespaces (DXML-4) - Fix pull-seq so it produces character events that work with emit-events (DXML-28) From 3de23261aa20b9567cf787bbbb3a2524bf12395f Mon Sep 17 00:00:00 2001 From: "Hudson @ build.clojure.org" Date: Sun, 28 Aug 2016 14:41:38 -0500 Subject: [PATCH 096/237] [maven-release-plugin] prepare release data.xml-0.1.0-beta2 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index d77dd01..d0abaea 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ 4.0.0 data.xml - 0.1.0-SNAPSHOT + 0.1.0-beta2 data.xml jar Functions to parse XML into lazy sequences and lazy trees and emit these as text @@ -39,7 +39,7 @@ scm:git:git@github.com:clojure/data.xml.git scm:git:git@github.com:clojure/data.xml.git git@github.com:clojure/data.xml.git - HEAD + data.xml-0.1.0-beta2 From 38229effff329daf57d4403f8a5836d20976aed7 Mon Sep 17 00:00:00 2001 From: "Hudson @ build.clojure.org" Date: Sun, 28 Aug 2016 14:41:38 -0500 Subject: [PATCH 097/237] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index d0abaea..d77dd01 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ 4.0.0 data.xml - 0.1.0-beta2 + 0.1.0-SNAPSHOT data.xml jar Functions to parse XML into lazy sequences and lazy trees and emit these as text @@ -39,7 +39,7 @@ scm:git:git@github.com:clojure/data.xml.git scm:git:git@github.com:clojure/data.xml.git git@github.com:clojure/data.xml.git - data.xml-0.1.0-beta2 + HEAD From 82fc4ca99139251586d1963b7e64f635148be9aa Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Fri, 30 Sep 2016 19:59:06 +0200 Subject: [PATCH 098/237] Fix emitting elements in the global namespace non-namespaced elements must be emitted within an empty default namespace `xmlns=""`, otherwise they would be in that namespace. --- .../clojure/clojure/data/xml/jvm/emit.clj | 22 ++++++++++++++++--- .../clojure/clojure/data/xml/test_names.clj | 6 +++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/clojure/data/xml/jvm/emit.clj b/src/main/clojure/clojure/data/xml/jvm/emit.clj index 72642fc..cd1c789 100644 --- a/src/main/clojure/clojure/data/xml/jvm/emit.clj +++ b/src/main/clojure/clojure/data/xml/jvm/emit.clj @@ -15,12 +15,15 @@ [clojure.string :as str]) (:import (java.io OutputStreamWriter Writer StringWriter) (java.nio.charset Charset) + (java.util.logging Logger Level) (javax.xml.namespace NamespaceContext) (javax.xml.stream XMLStreamWriter XMLOutputFactory) (javax.xml.transform OutputKeys Transformer TransformerFactory) (clojure.data.xml.event StartElementEvent EndElementEvent CharsEvent CDataEvent CommentEvent))) +(def logger (Logger/getLogger "clojure.data.xml")) + (defprotocol EventEmit (emit-event [event ^XMLStreamWriter writer])) @@ -61,7 +64,8 @@ tleft (reduce-kv (fn [tleft k v] (let [local (qname-local k)] (or (if (= "xmlns" local) - (when-not (= v (.. writer getNamespaceContext (getNamespaceURI ""))) + (when-not (= (str v) + (str (.. writer getNamespaceContext (getNamespaceURI "")))) (assoc! tleft v "")) (when-let [prefix (and (str/blank? (get-prefix writer tleft v)) (if (.. writer getNamespaceContext @@ -83,8 +87,20 @@ (defn- emit-start-tag [{:keys [attrs nss tag]} ^XMLStreamWriter writer] (let [uri (qname-uri tag) local (qname-local tag) - xmlns-attrs (xmlns-attribute-set writer nss (cons uri (map qname-uri (keys attrs))))] - (if (str/blank? uri) + global (str/blank? uri) + xmlns-attrs (xmlns-attribute-set + writer + (cond-> nss + global (as-> nss + (let [default (get nss :xmlns)] + (when (and + (not (str/blank? default)) + (.isLoggable logger Level/FINE)) + (.log logger Level/FINE + (format "Default `xmlns=\"%s\"` had to be replaced with a `xmlns=\"\"` because of global element `%s`" default local))) + (assoc nss :xmlns "")))) + (cons uri (map qname-uri (keys attrs))))] + (if global (.writeStartElement writer local) (.writeStartElement writer (get-prefix writer xmlns-attrs uri) local uri)) diff --git a/src/test/clojure/clojure/data/xml/test_names.clj b/src/test/clojure/clojure/data/xml/test_names.clj index 11b26dd..3be312d 100644 --- a/src/test/clojure/clojure/data/xml/test_names.clj +++ b/src/test/clojure/clojure/data/xml/test_names.clj @@ -55,3 +55,9 @@ ;; generated xmlns:a instead of xmlns:D (element ::V/other {:xmlns/D "uri-v:"})) (element ::D/limit {} (element ::V/other)))) + +(deftest test-preserve-empty-ns + (are [el] (= el (parse-str (emit-str (assoc-in el [:attrs :xmlns] "DAV:")))) + (element :top-level) + (element ::D/local-root {} + (element :top-level)))) From fa497c80468b882b20e9ac03dcbbc27cacbbd259 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Tue, 4 Oct 2016 00:17:05 +0200 Subject: [PATCH 099/237] Fix for clojure < 1.7 remove usage of cond-> and as-> --- src/main/clojure/clojure/data/xml/jvm/emit.clj | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/clojure/clojure/data/xml/jvm/emit.clj b/src/main/clojure/clojure/data/xml/jvm/emit.clj index cd1c789..2d0dfed 100644 --- a/src/main/clojure/clojure/data/xml/jvm/emit.clj +++ b/src/main/clojure/clojure/data/xml/jvm/emit.clj @@ -90,15 +90,15 @@ global (str/blank? uri) xmlns-attrs (xmlns-attribute-set writer - (cond-> nss - global (as-> nss - (let [default (get nss :xmlns)] - (when (and - (not (str/blank? default)) - (.isLoggable logger Level/FINE)) - (.log logger Level/FINE - (format "Default `xmlns=\"%s\"` had to be replaced with a `xmlns=\"\"` because of global element `%s`" default local))) - (assoc nss :xmlns "")))) + (if global + (let [default (get nss :xmlns)] + (when (and + (not (str/blank? default)) + (.isLoggable logger Level/FINE)) + (.log logger Level/FINE + (format "Default `xmlns=\"%s\"` had to be replaced with a `xmlns=\"\"` because of global element `%s`" default local))) + (assoc nss :xmlns "")) + nss) (cons uri (map qname-uri (keys attrs))))] (if global (.writeStartElement writer local) From 144f799507a5041a00ba683681ef41300eb7c9ca Mon Sep 17 00:00:00 2001 From: tamas Date: Thu, 24 Nov 2016 11:10:58 +0100 Subject: [PATCH 100/237] DXML-36: parse location-info into metadata --- README.md | 16 ++++ src/main/clojure/clojure/data/xml.clj | 5 +- src/main/clojure/clojure/data/xml/event.clj | 9 +- .../clojure/clojure/data/xml/jvm/parse.clj | 82 +++++++++++-------- .../clojure/clojure/data/xml/test_emit.clj | 7 +- .../clojure/clojure/data/xml/test_parse.clj | 13 +++ 6 files changed, 88 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 6d4acba..8d67580 100644 --- a/README.md +++ b/README.md @@ -323,6 +323,22 @@ Removing the metadata will cause the elements to not have a prefix, which is still correct, but will cause new prefixes to be generated when the document is emitted. +## Location information as meta + +By default the parser attaches location information as element meta, + `:character-offset`, `:column-number` and `:line-number` are available under + the `:clojure.data.xml/location-info` key: + + (deftest test-location-meta + (let [input "\n" + location-meta (comp :clojure.data.xml/location-info meta)] + (is (= 1 (-> input parse-str location-meta :line-number))) + +If you do not want to see the location info for any reason use an option to +prevent that: + + (parse-str your-input :location-info false) + ## License Licensed under the [Eclipse Public License](http://www.opensource.org/licenses/eclipse-1.0.php). diff --git a/src/main/clojure/clojure/data/xml.clj b/src/main/clojure/clojure/data/xml.clj index 87220e6..0ba241f 100644 --- a/src/main/clojure/clojure/data/xml.clj +++ b/src/main/clojure/clojure/data/xml.clj @@ -52,10 +52,11 @@ [source {:as props}] (let [props* (merge {:include-node? #{:element :characters} :coalescing true - :supporting-external-entities false} + :supporting-external-entities false + :location-info true} props)] (pull-seq (make-stream-reader props* source) - (get props* :include-node?) + props* nil))) (defn parse diff --git a/src/main/clojure/clojure/data/xml/event.clj b/src/main/clojure/clojure/data/xml/event.clj index 9cca608..fdae476 100644 --- a/src/main/clojure/clojure/data/xml/event.clj +++ b/src/main/clojure/clojure/data/xml/event.clj @@ -27,7 +27,7 @@ ; Represents a parse event. ; type is one of :start-element, :end-element, or :characters -(defrecord StartElementEvent [tag attrs nss]) +(defrecord StartElementEvent [tag attrs nss location-info]) (defrecord EndElementEvent [tag]) (defrecord CharsEvent [str]) (defrecord CDataEvent [str]) @@ -40,7 +40,7 @@ {:gen-event (fn elem-gen-event [{:keys [tag attrs] :as element}] (separate-xmlns attrs #(->StartElementEvent - tag %1 (merge-nss (element-nss* element) %2)))) + tag %1 (merge-nss (element-nss* element) %2) nil))) :next-events (fn elem-next-events [{:keys [tag content]} next-items] (list* content (->EndElementEvent tag) next-items))}] (extend-protocol-fns @@ -74,7 +74,10 @@ (defn event-element [event contents] (when (instance? StartElementEvent event) (element* (:tag event) (:attrs event) contents - {:clojure.data.xml/nss (:nss event)}))) + (if-let [loc (:location-info event)] + {:clojure.data.xml/location-info loc + :clojure.data.xml/nss (:nss event)} + {:clojure.data.xml/nss (:nss event)})))) (defn event-node [event] (cond diff --git a/src/main/clojure/clojure/data/xml/jvm/parse.clj b/src/main/clojure/clojure/data/xml/jvm/parse.clj index 4821974..5c5154f 100644 --- a/src/main/clojure/clojure/data/xml/jvm/parse.clj +++ b/src/main/clojure/clojure/data/xml/jvm/parse.clj @@ -55,49 +55,59 @@ (transient parent-hash) (range (.getNamespaceCount sreader))))) +(defn- location-hash + [^XMLStreamReader sreader] + (when-let [location (.getLocation sreader)] + {:character-offset (.getCharacterOffset location) + :column-number (.getColumnNumber location) + :line-number (.getLineNumber location)})) + ; Note, sreader is mutable and mutated here in pull-seq, but it's ; protected by a lazy-seq so it's thread-safe. (defn pull-seq "Creates a seq of events. The XMLStreamConstants/SPACE clause below doesn't seem to be triggered by the JDK StAX parser, but is by others. Leaving in to be more complete." - [^XMLStreamReader sreader include-node? ns-envs] + [^XMLStreamReader sreader {:keys [include-node? location-info] :as opts} ns-envs] (lazy-seq (loop [] - (static-case - (.next sreader) - ; condp == (.next sreader) - XMLStreamConstants/START_ELEMENT - (if (include-node? :element) - (let [ns-env (nss-hash sreader (or (first ns-envs) {}))] - (cons (->StartElementEvent (canonical-name (.getNamespaceURI sreader) - (.getLocalName sreader) - (.getPrefix sreader)) - (attr-hash sreader) - ns-env) - (pull-seq sreader include-node? (cons ns-env ns-envs)))) - (recur)) - XMLStreamConstants/END_ELEMENT - (if (include-node? :element) - (do (assert (seq ns-envs) "Balanced end") - (cons (->EndElementEvent (keyword (.getLocalName sreader))) - (pull-seq sreader include-node? (rest ns-envs)))) - (recur)) - XMLStreamConstants/CHARACTERS - (if-let [text (and (include-node? :characters) - (not (.isWhiteSpace sreader)) - (.getText sreader))] - (cons (->CharsEvent text) - (pull-seq sreader include-node? ns-envs)) - (recur)) - XMLStreamConstants/COMMENT - (if (include-node? :comment) - (cons (->CommentEvent (.getText sreader)) - (pull-seq sreader include-node? ns-envs)) - (recur)) - XMLStreamConstants/END_DOCUMENT - nil - ;; Consume and ignore comments, spaces, processing instructions etc - (recur))))) + (let [location (when location-info + (location-hash sreader))] + (static-case + (.next sreader) + ; condp == (.next sreader) + XMLStreamConstants/START_ELEMENT + (if (include-node? :element) + (let [ns-env (nss-hash sreader (or (first ns-envs) {}))] + (cons (->StartElementEvent (canonical-name (.getNamespaceURI sreader) + (.getLocalName sreader) + (.getPrefix sreader)) + (attr-hash sreader) + ns-env + location) + (pull-seq sreader opts (cons ns-env ns-envs)))) + (recur)) + XMLStreamConstants/END_ELEMENT + (if (include-node? :element) + (do (assert (seq ns-envs) "Balanced end") + (cons (->EndElementEvent (keyword (.getLocalName sreader))) + (pull-seq sreader opts (rest ns-envs)))) + (recur)) + XMLStreamConstants/CHARACTERS + (if-let [text (and (include-node? :characters) + (not (.isWhiteSpace sreader)) + (.getText sreader))] + (cons (->CharsEvent text) + (pull-seq sreader opts ns-envs)) + (recur)) + XMLStreamConstants/COMMENT + (if (include-node? :comment) + (cons (->CommentEvent (.getText sreader)) + (pull-seq sreader opts ns-envs)) + (recur)) + XMLStreamConstants/END_DOCUMENT + nil + ;; Consume and ignore comments, spaces, processing instructions etc + (recur)))))) (defn- make-input-factory [props] (let [fac (XMLInputFactory/newInstance)] diff --git a/src/test/clojure/clojure/data/xml/test_emit.clj b/src/test/clojure/clojure/data/xml/test_emit.clj index 10fb658..65053ab 100644 --- a/src/test/clojure/clojure/data/xml/test_emit.clj +++ b/src/test/clojure/clojure/data/xml/test_emit.clj @@ -186,6 +186,7 @@ (is (= (parse-str (emit-str el)) el)))) (deftest test-default-xmlns - (is (= {:clojure.data.xml/nss {:xmlns "NS"}} - (meta (parse-str "")) - (meta (parse-str (emit-str (parse-str ""))))))) + (let [nss-meta (comp :clojure.data.xml/nss meta)] + (is (= {:xmlns "NS"} + (nss-meta (parse-str "")) + (nss-meta (parse-str (emit-str (parse-str "")))))))) diff --git a/src/test/clojure/clojure/data/xml/test_parse.clj b/src/test/clojure/clojure/data/xml/test_parse.clj index 65bc77b..da420c5 100644 --- a/src/test/clojure/clojure/data/xml/test_parse.clj +++ b/src/test/clojure/clojure/data/xml/test_parse.clj @@ -80,3 +80,16 @@ (is (= ["\nfoo bar\n\nbaz\n"] (:content (parse-str input)))) (is (= ["\nfoo bar\n" "\nbaz\n"] (:content (parse-str input :coalescing false)))))) + +(deftest test-location-meta + (let [input "\n" + location-meta (comp :clojure.data.xml/location-info meta)] + ;the numbers look 1 based + (is (= 1 (-> input parse-str location-meta :line-number))) + (is (= 1 (-> input parse-str location-meta :column-number))) + (is (= 1 (-> input parse-str :content first location-meta :line-number))) + (is (= 4 (-> input parse-str :content first location-meta :column-number))) + (is (= 2 (-> input parse-str :content second location-meta :line-number))) + (is (nil? (-> input + (parse-str :location-info false) + location-meta))))) From b59d69c1902581bfbb33963bbe1da132f723688d Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Sun, 27 Nov 2016 17:59:26 +0100 Subject: [PATCH 101/237] Prepare docs for 0.1.0-beta3 --- CHANGES.md | 6 +++++- README.md | 9 ++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 77ffa80..2ceef9a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,7 @@ +From 0.1.0-beta2 to 0.1.0-beta3 +- Fix emitter to keep non-namespaced xml names out of any set default namespace +- Add support for location info in parser + From 0.1.0-beta1 to 0.1.0-beta2 - Add support for emitting DOCTYPEs (DXML-10) - Fix issue emitting sibling namespaces (DXML-33) @@ -20,4 +24,4 @@ From 0.0.6 to 0.0.7 - Strings now supported as tag names, previously was only kewords (DXML-8) - Add CDATA and comments support to sexp-as-element (DXML-11) - data.xml now properly handles CDATA records that contain an embedded ]]> - by breaking it into two CDATA sections (DXML-12) \ No newline at end of file + by breaking it into two CDATA sections (DXML-12) diff --git a/README.md b/README.md index 8d67580..402dbd6 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ Add the following to the `project.clj` dependencies: ## Installation - Beta -Latest beta release: 0.1.0-beta1 +Latest beta release: 0.1.0-beta3 * [All Released Versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22data.xml%22) @@ -51,13 +51,13 @@ For Maven projects, add the following XML in your `pom.xml`'s `` s org.clojure data.xml - 0.1.0-beta1 + 0.1.0-beta3 ### Leiningen Add the following to the `project.clj` dependencies: - [org.clojure/data.xml "0.1.0-beta1"] + [org.clojure/data.xml "0.1.0-beta3"] ## Examples @@ -334,8 +334,7 @@ By default the parser attaches location information as element meta, location-meta (comp :clojure.data.xml/location-info meta)] (is (= 1 (-> input parse-str location-meta :line-number))) -If you do not want to see the location info for any reason use an option to -prevent that: +To elide location information, pass `:location-info false` to the parser: (parse-str your-input :location-info false) From 9e9fe5f991bd60ad9ba6ba09b5f3cf528017752e Mon Sep 17 00:00:00 2001 From: "Hudson @ build.clojure.org" Date: Sun, 4 Dec 2016 16:55:47 -0600 Subject: [PATCH 102/237] [maven-release-plugin] prepare release data.xml-0.1.0-beta3 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index d77dd01..e5a889f 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ 4.0.0 data.xml - 0.1.0-SNAPSHOT + 0.1.0-beta3 data.xml jar Functions to parse XML into lazy sequences and lazy trees and emit these as text @@ -39,7 +39,7 @@ scm:git:git@github.com:clojure/data.xml.git scm:git:git@github.com:clojure/data.xml.git git@github.com:clojure/data.xml.git - HEAD + data.xml-0.1.0-beta3 From 456c87e916c2bc027b2642d0d6a6b53d0e6fdb84 Mon Sep 17 00:00:00 2001 From: "Hudson @ build.clojure.org" Date: Sun, 4 Dec 2016 16:55:47 -0600 Subject: [PATCH 103/237] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e5a889f..aca7914 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ 4.0.0 data.xml - 0.1.0-beta3 + 0.2.0-SNAPSHOT data.xml jar Functions to parse XML into lazy sequences and lazy trees and emit these as text @@ -39,7 +39,7 @@ scm:git:git@github.com:clojure/data.xml.git scm:git:git@github.com:clojure/data.xml.git git@github.com:clojure/data.xml.git - data.xml-0.1.0-beta3 + HEAD From d1d2b8d49326038247e9876523ac51f337e55fa2 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Fri, 15 Apr 2016 16:02:13 +0200 Subject: [PATCH 104/237] Partial clojurescriopt support qnames, emit-str, parse-str, read dom nodes through clj protocols --- src/main/clojure/clojure/data/xml.cljs | 99 +++++++++++++++++++ .../clojure/clojure/data/xml/js/name.cljs | 24 +++++ .../clojure/clojure/data/xml/jvm/name.clj | 5 - .../clojure/data/xml/{name.clj => name.cljc} | 99 +++++++++++-------- .../xml/{protocols.clj => protocols.cljc} | 0 5 files changed, 183 insertions(+), 44 deletions(-) create mode 100644 src/main/clojure/clojure/data/xml.cljs create mode 100644 src/main/clojure/clojure/data/xml/js/name.cljs rename src/main/clojure/clojure/data/xml/{name.clj => name.cljc} (65%) rename src/main/clojure/clojure/data/xml/{protocols.clj => protocols.cljc} (100%) diff --git a/src/main/clojure/clojure/data/xml.cljs b/src/main/clojure/clojure/data/xml.cljs new file mode 100644 index 0000000..9312e6d --- /dev/null +++ b/src/main/clojure/clojure/data/xml.cljs @@ -0,0 +1,99 @@ +(ns clojure.data.xml + (:require-macros + [clojure.data.xml.impl :refer [export-api]]) + (:require + [clojure.data.xml.name :as name] + [clojure.data.xml.protocols :refer [AsQName]])) + +(export-api + name/ns-uri name/uri-ns name/declare-ns name/alias-ns + name/parse-qname name/qname-uri name/qname-local name/qname name/to-qname) + +(defn canonical-name + "Put (q)name into canonical form as per ns-env" + [n] + (name/canonical-name (qname-uri n) (qname-local n) "")) + +;; TODO event-seq +;; TODO parse (use goog StringBuffer?) + +(defn parse-str + "Use DOMParser to parse xml string" + ;; TODO detect browser specific parsererror tags + ;; see http://stackoverflow.com/questions/11563554/how-do-i-detect-xml-parsing-errors-when-using-javascripts-domparser-in-a-cross + [s & {:keys [content-type on-error] + :or {content-type "text/xml" + on-error #(throw "XML parser error" {:doc % :input s})}}] + (let [dom (. (js/DOMParser.) + (parseFromString s content-type)) + doc (.-documentElement dom)] + (if (= "parsererror" (.-nodeName doc)) + (on-error doc) + doc))) + +;; TODO emit (use goog StringBuffer?) + +(defn emit-str + "Use XMLSerializer to render an xml string" + [e & {:keys []}] + (. (js/XMLSerializer.) + (serializeToString e))) + +(defn extend-nodes-as-qname! [] + (extend-protocol AsQName + js/Element + (qname-local [e] (.-localName e)) + (qname-uri [e] (.-namespaceURI e)) + js/Attr + (qname-local [e] (.-localName e)) + (qname-uri [e] (.-namespaceURI e)))) + +(defn extend-dom-as-data! [] + (extend-type js/Element + ILookup + (-lookup [el k] + (case k + :tag (name/canonical-name (.-namespaceURI el) + (.-localName el) + "") + :attrs (.-attributes el) + :content (.-childNodes el) + (throw "XML tag has no key" {:key k :el el})))) + (extend-type js/NamedNodeMap + ILookup + (-lookup + ([attrs attr] + (if-let [i (.getNamedItemNS attrs (qname-uri attr) (qname-local attr))] + (.-value i) + nil)) + ([attrs attr not-found] + (if-let [i (.getNamedItemNS attrs (qname-uri attr) (qname-local attr))] + (.-value i) + not-found)))) + (extend-type js/NodeList + ISeqable + (-seq [nl] (array-seq nl)) + ISequential + ICounted + (-count [nl] (.-length nl)) + IIndexed + (-nth + ([nl n] + (aget nl n)) + ([nl n nf] + (if (and (<= 0 n) (< n (.-length nl))) + (let [res (aget nl n)] + (if (instance? js/Text res) + (.-data res) + res)) + nf))))) + +(do ;comment + + (extend-dom-as-data!) + (extend-nodes-as-qname!) + + (let [{tag :tag {a1 "{DAV:}a1" a2 "a2" :as attrs} :attrs [c1 c2 c3] :content} xml] + [tag a1 a2 c1 c2 c3]) + + ) diff --git a/src/main/clojure/clojure/data/xml/js/name.cljs b/src/main/clojure/clojure/data/xml/js/name.cljs new file mode 100644 index 0000000..9d9e2f1 --- /dev/null +++ b/src/main/clojure/clojure/data/xml/js/name.cljs @@ -0,0 +1,24 @@ +(ns clojure.data.xml.js.name + (:require [clojure.data.xml.protocols :refer [AsQName qname-uri qname-local]] + [clojure.string :as str])) + +(defrecord QName [uri local prefix] + AsQName + (qname-local [_] local) + (qname-uri [_] uri)) + +(specify! (.-prototype QName) + IEquiv + (-equiv [this other] + (and (instance? QName other) + (= (.-local this) (.-local other)) + (= (.-uri this) (.-uri other))))) + +(defn make-qname [uri name prefix] + (->QName uri name prefix)) + +(def parse-qname + (memoize + (fn [s] + (let [[_ ns-uri local] (re-matches #"(?:\{([^}]+)\})?([^{]*)" s)] + (make-qname (or ns-uri "") local ""))))) diff --git a/src/main/clojure/clojure/data/xml/jvm/name.clj b/src/main/clojure/clojure/data/xml/jvm/name.clj index 1d9d6fc..fdd007d 100644 --- a/src/main/clojure/clojure/data/xml/jvm/name.clj +++ b/src/main/clojure/clojure/data/xml/jvm/name.clj @@ -26,8 +26,3 @@ (definline make-qname [uri name prefix] `(QName. ~uri ~name ~prefix)) - -(defn qname - ([name] (make-qname "" name "")) - ([uri name] (make-qname (or uri "") name "")) - ([uri name prefix] (make-qname (or uri "") name (or prefix "")))) diff --git a/src/main/clojure/clojure/data/xml/name.clj b/src/main/clojure/clojure/data/xml/name.cljc similarity index 65% rename from src/main/clojure/clojure/data/xml/name.clj rename to src/main/clojure/clojure/data/xml/name.cljc index ab8593b..ea7bc17 100644 --- a/src/main/clojure/clojure/data/xml/name.clj +++ b/src/main/clojure/clojure/data/xml/name.cljc @@ -7,17 +7,30 @@ ; You must not remove this notice, or any other, from this software. (ns clojure.data.xml.name - (:require [clojure.string :as str] - [clojure.data.xml.jvm.name :as jvm] - (clojure.data.xml - [impl :refer [export-api]] - [protocols :as protocols :refer [AsQName]])) - (:import (clojure.lang Namespace Keyword))) + #?@(:clj [(:require [clojure.string :as str] + [clojure.data.xml.jvm.name :as jvm] + (clojure.data.xml + [impl :refer [export-api]] + [protocols :as protocols :refer [AsQName]])) + (:import (clojure.lang Namespace Keyword))] + :cljs [(:require-macros + [clojure.data.xml.impl :refer [export-api]]) + (:require [clojure.string :as str] + [clojure.data.xml.js.name :as jsn] + [clojure.data.xml.protocols :as protocols :refer [AsQName]]) + (:import (goog.string StringBuffer))])) (export-api - jvm/parse-qname jvm/make-qname jvm/qname + #?@(:clj [jvm/parse-qname jvm/make-qname] + :cljs [jsn/parse-qname jsn/make-qname]) + ;; protocol functions can be redefined by extend-* - #'protocols/qname-uri #'protocols/qname-local) + protocols/qname-uri protocols/qname-local) + +(defn qname + ([name] (make-qname "" name "")) + ([uri name] (make-qname (or uri "") name "")) + ([uri name prefix] (make-qname (or uri "") name (or prefix "")))) ;; The empty string shall be equal to nil for xml names (defn namespaced? [qn] @@ -43,7 +56,7 @@ (-> @nss :xs->ns (get uri))) (extend-protocol AsQName - clojure.lang.Keyword + Keyword (qname-local [kw] (name kw)) (qname-uri [kw] (if-let [ns (namespace kw)] @@ -51,21 +64,20 @@ (throw (ex-info (str "Unknown xmlns for clj ns: " (pr-str ns)) {:qname kw}))) "")) - String + #?(:clj String :cljs string) (qname-local [s] (qname-local (parse-qname s))) (qname-uri [s] (qname-uri (parse-qname s)))) -(definline canonical-name [uri local prefix] - `(let [uri# ~uri local# ~local] - (if (str/blank? uri#) - (keyword local#) - (let [kw-prefix# (uri-ns uri#)] - (if (str/blank? kw-prefix#) - (make-qname uri# local# ~prefix) - (keyword kw-prefix# local#)))))) +(defn canonical-name [uri local prefix] + (if (str/blank? uri) + (keyword local) + (let [kw-prefix (uri-ns uri)] + (if (str/blank? kw-prefix) + (make-qname uri local prefix) + (keyword kw-prefix local))))) -(definline to-qname [n] - `(let [n# ~n] (make-qname (or (qname-uri n#) "") (qname-local n#) ""))) +(defn to-qname [n] + (make-qname (or (qname-uri n) "") (qname-local n) "")) (defn- declare-ns* [{:keys [ns->xs xs->ns] :as acc} [ns xmlns & rst :as nss]] (if (seq nss) @@ -96,30 +108,33 @@ :xml "http://www.w3.org/XML/1998/namespace" :xmlns "http://www.w3.org/2000/xmlns/") -(def ^:const empty-namespace +(def ;;once ^:const + empty-namespace {"xml" (ns-uri "xml") "xmlns" (ns-uri "xmlns")}) -(def ^:const xmlns-uri (ns-uri "xmlns")) +(def ;;once ^:const + xmlns-uri (ns-uri "xmlns")) -(defn alias-ns - "Define a clojure namespace alias for shortened keyword and symbol namespaces. +#?(:clj + (defn alias-ns + "Define a clojure namespace alias for shortened keyword and symbol namespaces. Similar to clojure.core/alias, but if namespace doesn't exist, it is created. ## Example (declare-ns :xml.dav \"DAV:\") (alias-ns :D :xml.dav) {:tag ::D/propfind :content []}" - {:arglists '([& {:as alias-nss}])} - [& ans] - (loop [[a n & rst :as ans] ans] - (when (seq ans) - (assert (<= 2 (count ans)) (pr-str ans)) - (let [ns (symbol (clj-ns-name n)) - al (symbol (clj-ns-name a))] - (create-ns ns) - (alias al ns) - (recur rst))))) + {:arglists '([& {:as alias-nss}])} + [& ans] + (loop [[a n & rst :as ans] ans] + (when (seq ans) + (assert (<= 2 (count ans)) (pr-str ans)) + (let [ns (symbol (clj-ns-name n)) + al (symbol (clj-ns-name a))] + (create-ns ns) + (alias al ns) + (recur rst)))))) (defn merge-nss "Merge two attribute sets, deleting assignments of empty-string" @@ -160,10 +175,14 @@ ;(set! *warn-on-reflection* true) -(def ^:private ^"[C" prefix-alphabet - (char-array - (map char - (range (int \a) (inc (int \z)))))) +#?(:clj (def ^:private ^"[C" prefix-alphabet + (char-array + (map char + (range (int \a) (inc (int \z)))))) + :cljs (def ^:private prefix-alphabet + (apply str (map js/String.fromCharCode + (range (.charCodeAt "a" 0) + (inc (.charCodeAt "z" 0))))))) (def ^{:dynamic true :doc "Thread local counter for a single document"} @@ -173,11 +192,13 @@ "Generates an xml prefix. Zero-arity can only be called, when *gen-prefix-counter* is bound and will increment it." ([] (let [c *gen-prefix-counter*] + (when (undefined? c) + (throw (ex-info "Not bound: *gen-prefix-counter*" {:v #'*gen-prefix-counter*}))) (set! *gen-prefix-counter* (inc c)) (gen-prefix c))) ([n] (let [cnt (alength prefix-alphabet) - sb (StringBuilder.)] + sb #?(:clj (StringBuilder.) :cljs (StringBuffer.))] (loop [n* n] (let [ch (mod n* cnt) n** (quot n* cnt)] diff --git a/src/main/clojure/clojure/data/xml/protocols.clj b/src/main/clojure/clojure/data/xml/protocols.cljc similarity index 100% rename from src/main/clojure/clojure/data/xml/protocols.clj rename to src/main/clojure/clojure/data/xml/protocols.cljc From 852fc7b9eba45329d9e80d64a851cd7d657521a6 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Sat, 16 Apr 2016 17:52:28 +0200 Subject: [PATCH 105/237] add node creation implemention for cljs --- src/main/clojure/clojure/data/xml.cljs | 13 ++++-- src/main/clojure/clojure/data/xml/node.cljs | 51 +++++++++++++++++++++ 2 files changed, 60 insertions(+), 4 deletions(-) create mode 100644 src/main/clojure/clojure/data/xml/node.cljs diff --git a/src/main/clojure/clojure/data/xml.cljs b/src/main/clojure/clojure/data/xml.cljs index 9312e6d..3da03fc 100644 --- a/src/main/clojure/clojure/data/xml.cljs +++ b/src/main/clojure/clojure/data/xml.cljs @@ -3,11 +3,13 @@ [clojure.data.xml.impl :refer [export-api]]) (:require [clojure.data.xml.name :as name] + [clojure.data.xml.node :as node] [clojure.data.xml.protocols :refer [AsQName]])) (export-api - name/ns-uri name/uri-ns name/declare-ns name/alias-ns - name/parse-qname name/qname-uri name/qname-local name/qname name/to-qname) + name/ns-uri name/uri-ns name/declare-ns + name/parse-qname name/qname-uri name/qname-local name/qname name/to-qname + node/element* node/element node/cdata node/xml-comment) (defn canonical-name "Put (q)name into canonical form as per ns-env" @@ -88,12 +90,15 @@ res)) nf))))) -(do ;comment +(comment (extend-dom-as-data!) (extend-nodes-as-qname!) - (let [{tag :tag {a1 "{DAV:}a1" a2 "a2" :as attrs} :attrs [c1 c2 c3] :content} xml] + (let [{tag :tag {a1 "{DAV:}a1" a2 "a2" :as attrs} :attrs [c1 c2 c3] :content} + (parse-str " +Fancy Content
More +
")] [tag a1 a2 c1 c2 c3]) ) diff --git a/src/main/clojure/clojure/data/xml/node.cljs b/src/main/clojure/clojure/data/xml/node.cljs new file mode 100644 index 0000000..cc18531 --- /dev/null +++ b/src/main/clojure/clojure/data/xml/node.cljs @@ -0,0 +1,51 @@ +(ns clojure.data.xml.node + (:require + [clojure.data.xml.name :refer [qname-uri qname-local]])) + +(def doc + (.. (js/DOMParser.) + (parseFromString "" "text/xml"))) + +(defn element* + "Create an xml element from a content collection and optional metadata" + ([tag attrs content meta] + (let [el (element* tag attrs content)] + (specify! el + IMeta + (-meta [_] meta) + IWithMeta + (-with-meta [_ meta] + (specify el + IMeta + (-meta [_] meta) + IWithMeta + (-with-meta [_ meta] + (-with-meta el meta))))) + el)) + ([tag attrs content] + (let [el (.createElementNS doc (qname-uri tag) (qname-local tag))] + (reduce-kv (fn [_ k v] + (.setAttributeNS el (qname-uri k) (qname-local k) v)) + nil attrs) + (reduce (fn [_ n] + (.appendChild el (if (string? n) + (.createTextNode doc n) + n))) + nil content) + el))) + +(defn element + "Create an xml Element from content varargs" + ([tag] (element* tag nil nil)) + ([tag attrs] (element* tag attrs nil)) + ([tag attrs & content] (element* tag attrs content))) + +(defn cdata + "Create a CData node" + [content] + (.createCDATASection doc content)) + +(defn xml-comment + "Create a Comment node" + [content] + (.createComment doc content)) From 5054265ebc14a000f0fe5bca7832258a5b1dc578 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Sat, 16 Apr 2016 17:52:45 +0200 Subject: [PATCH 106/237] Fix prefix counter for clj --- src/main/clojure/clojure/data/xml/name.cljc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/clojure/data/xml/name.cljc b/src/main/clojure/clojure/data/xml/name.cljc index ea7bc17..50f0820 100644 --- a/src/main/clojure/clojure/data/xml/name.cljc +++ b/src/main/clojure/clojure/data/xml/name.cljc @@ -192,8 +192,8 @@ "Generates an xml prefix. Zero-arity can only be called, when *gen-prefix-counter* is bound and will increment it." ([] (let [c *gen-prefix-counter*] - (when (undefined? c) - (throw (ex-info "Not bound: *gen-prefix-counter*" {:v #'*gen-prefix-counter*}))) + #?(:cljs (when (undefined? c) + (throw (ex-info "Not bound: *gen-prefix-counter*" {:v #'*gen-prefix-counter*})))) (set! *gen-prefix-counter* (inc c)) (gen-prefix c))) ([n] From fc3b701e58523dd6a5f231fec31cc608349e8831 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Tue, 26 Apr 2016 06:32:25 +0200 Subject: [PATCH 107/237] fix emission of xmlns attrs in cljs --- project.clj | 2 +- src/main/clojure/clojure/data/xml/node.cljs | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/project.clj b/project.clj index 96b7f11..7f34dac 100644 --- a/project.clj +++ b/project.clj @@ -2,4 +2,4 @@ :source-paths ["src/main/clojure"] :test-paths ["src/test/clojure"] :resource-paths ["src/test/resources"] - :dependencies [[org.clojure/clojure "1.8.0-RC3"]]) + :dependencies [[org.clojure/clojure "1.8.0"]]) diff --git a/src/main/clojure/clojure/data/xml/node.cljs b/src/main/clojure/clojure/data/xml/node.cljs index cc18531..af85b64 100644 --- a/src/main/clojure/clojure/data/xml/node.cljs +++ b/src/main/clojure/clojure/data/xml/node.cljs @@ -25,7 +25,10 @@ ([tag attrs content] (let [el (.createElementNS doc (qname-uri tag) (qname-local tag))] (reduce-kv (fn [_ k v] - (.setAttributeNS el (qname-uri k) (qname-local k) v)) + (let [uri (qname-uri k)] + (if (= uri "http://www.w3.org/2000/xmlns/") + (.setAttribute el (str "xmlns:" (qname-local k)) v) + (.setAttributeNS el uri (qname-local k) v)))) nil attrs) (reduce (fn [_ n] (.appendChild el (if (string? n) From 60b10227b005c098218e245acc051f2f2a9b7c0a Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Sun, 8 May 2016 13:46:28 +0200 Subject: [PATCH 108/237] cljs: conversion fixes --- src/main/clojure/clojure/data/xml.cljs | 17 ++++++++++------- src/main/clojure/clojure/data/xml/node.cljs | 9 +++++++++ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/main/clojure/clojure/data/xml.cljs b/src/main/clojure/clojure/data/xml.cljs index 3da03fc..54c0c3e 100644 --- a/src/main/clojure/clojure/data/xml.cljs +++ b/src/main/clojure/clojure/data/xml.cljs @@ -39,7 +39,8 @@ "Use XMLSerializer to render an xml string" [e & {:keys []}] (. (js/XMLSerializer.) - (serializeToString e))) + (serializeToString + (node/coerce-to-dom e)))) (defn extend-nodes-as-qname! [] (extend-protocol AsQName @@ -50,6 +51,11 @@ (qname-local [e] (.-localName e)) (qname-uri [e] (.-namespaceURI e)))) +(defn- as-node [n] + (if (instance? js/Text n) + (.-wholeText n) ;; .-data + n)) + (defn extend-dom-as-data! [] (extend-type js/Element ILookup @@ -74,20 +80,17 @@ not-found)))) (extend-type js/NodeList ISeqable - (-seq [nl] (array-seq nl)) + (-seq [nl] (map as-node (array-seq nl))) ISequential ICounted (-count [nl] (.-length nl)) IIndexed (-nth ([nl n] - (aget nl n)) + (as-node (aget nl n))) ([nl n nf] (if (and (<= 0 n) (< n (.-length nl))) - (let [res (aget nl n)] - (if (instance? js/Text res) - (.-data res) - res)) + (as-node (aget nl n)) nf))))) (comment diff --git a/src/main/clojure/clojure/data/xml/node.cljs b/src/main/clojure/clojure/data/xml/node.cljs index af85b64..e038699 100644 --- a/src/main/clojure/clojure/data/xml/node.cljs +++ b/src/main/clojure/clojure/data/xml/node.cljs @@ -52,3 +52,12 @@ "Create a Comment node" [content] (.createComment doc content)) + +(defn coerce-to-dom [node] + (cond + (string? node) node + (instance? js/Element node) node + (satisfies? ILookup node) (element* (:tag node) + (:attrs node) + (map coerce-to-dom (:content node))) + :else (throw (ex-info "Cannot coerce" {:form node})))) From ae41714816bd5d6dbd9dad4a252d6b16731825d2 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Mon, 9 May 2016 07:31:03 +0200 Subject: [PATCH 109/237] wrappers for qname-* protocol functions --- src/main/clojure/clojure/data/xml/name.cljc | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/clojure/data/xml/name.cljc b/src/main/clojure/clojure/data/xml/name.cljc index 50f0820..b8e0444 100644 --- a/src/main/clojure/clojure/data/xml/name.cljc +++ b/src/main/clojure/clojure/data/xml/name.cljc @@ -22,10 +22,20 @@ (export-api #?@(:clj [jvm/parse-qname jvm/make-qname] - :cljs [jsn/parse-qname jsn/make-qname]) - - ;; protocol functions can be redefined by extend-* - protocols/qname-uri protocols/qname-local) + :cljs [jsn/parse-qname jsn/make-qname])) + +;; protocol functions can be redefined by extend-*, so we wrap +;; protocols/qname-uri protocols/qname-local within regular fns + +(defn qname-uri + "Get the namespace uri for this qname" + [v] + (protocols/qname-uri v)) + +(defn qname-local + "Get the name for this qname" + [v] + (protocols/qname-local v)) (defn qname ([name] (make-qname "" name "")) From ea8babc26e487db297717fab7d65d059fe02414d Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Thu, 29 Sep 2016 01:48:00 +0200 Subject: [PATCH 110/237] Prototype urlencoded xmlns encoding [BREAKING] Remove `declare-ns` and `alias-ns` along with the global namespace registry. Introduce `alias-uri` to directly alias xmlns to clj-ns. == Open issues at this point - will '% be added to the ist of officially allowed clojure symbols? - since . isn't percent-encoded, (as per https://tools.ietf.org/html/rfc3986#section-2.2) creating awkward subdirectories was nessecary for predefined namespace files for cljs. Should we interpret the rfc more freely and also percent-encode the dot? (requires custom encoders) - There needs to be a reader tag for encoding an xml namespaces to a symbol - safe percent encoding (#xml/ns, maybe?) this would allow (:require [#xml/ns "http://..xhtml.." :as xhtml]) - Cljs needs a more comprehensive collection of predefined xmlns _or_ clojure.core/alias could be changed to create aliased namespaces on the fly. --- src/main/clojure/clojure/data/xml.clj | 2 +- .../clojure/clojure/data/xml/js/name.cljs | 6 + .../clojure/clojure/data/xml/jvm/name.clj | 9 +- src/main/clojure/clojure/data/xml/name.cljc | 107 +++++++----------- .../w3/org%2F2000%2Fxmlns%2F.cljc | 7 ++ .../w3/org%2FXML%2F1998%2Fnamespace.cljc | 7 ++ .../clojure/clojure/data/xml/test_emit.clj | 10 +- .../clojure/clojure/data/xml/test_names.clj | 26 ++--- 8 files changed, 82 insertions(+), 92 deletions(-) create mode 100644 src/main/clojure/xmlns/http%3A%2F%2Fwww/w3/org%2F2000%2Fxmlns%2F.cljc create mode 100644 src/main/clojure/xmlns/http%3A%2F%2Fwww/w3/org%2FXML%2F1998%2Fnamespace.cljc diff --git a/src/main/clojure/clojure/data/xml.clj b/src/main/clojure/clojure/data/xml.clj index 0ba241f..09f29f5 100644 --- a/src/main/clojure/clojure/data/xml.clj +++ b/src/main/clojure/clojure/data/xml.clj @@ -33,7 +33,7 @@ (export-api node/element* node/element node/cdata node/xml-comment prxml/sexp-as-element prxml/sexps-as-fragment event/element-nss - name/ns-uri name/uri-ns name/declare-ns name/alias-ns + #_name/ns-uri #_name/uri-ns #_name/declare-ns #_name/alias-ns name/alias-uri name/parse-qname name/qname-uri name/qname-local name/qname name/to-qname process/find-xmlns process/aggregate-xmlns) diff --git a/src/main/clojure/clojure/data/xml/js/name.cljs b/src/main/clojure/clojure/data/xml/js/name.cljs index 9d9e2f1..0841017 100644 --- a/src/main/clojure/clojure/data/xml/js/name.cljs +++ b/src/main/clojure/clojure/data/xml/js/name.cljs @@ -22,3 +22,9 @@ (fn [s] (let [[_ ns-uri local] (re-matches #"(?:\{([^}]+)\})?([^{]*)" s)] (make-qname (or ns-uri "") local ""))))) + +(defn decode-uri [ns] + (js/decodeURIComponent ns)) + +(defn encode-uri [uri] + (js/encodeURIComponent uri)) diff --git a/src/main/clojure/clojure/data/xml/jvm/name.clj b/src/main/clojure/clojure/data/xml/jvm/name.clj index fdd007d..ba2b424 100644 --- a/src/main/clojure/clojure/data/xml/jvm/name.clj +++ b/src/main/clojure/clojure/data/xml/jvm/name.clj @@ -11,7 +11,8 @@ [protocols :refer [AsQName qname-uri qname-local]]) [clojure.string :as str]) (:import java.io.Writer - (javax.xml.namespace NamespaceContext QName))) + (javax.xml.namespace NamespaceContext QName) + (java.net URLEncoder URLDecoder))) (extend-protocol AsQName QName @@ -26,3 +27,9 @@ (definline make-qname [uri name prefix] `(QName. ~uri ~name ~prefix)) + +(definline decode-uri [ns] + `(URLDecoder/decode ~ns "UTF-8")) + +(definline encode-uri [uri] + `(URLEncoder/encode ~uri "UTF-8")) diff --git a/src/main/clojure/clojure/data/xml/name.cljc b/src/main/clojure/clojure/data/xml/name.cljc index b8e0444..eaba822 100644 --- a/src/main/clojure/clojure/data/xml/name.cljc +++ b/src/main/clojure/clojure/data/xml/name.cljc @@ -21,8 +21,8 @@ (:import (goog.string StringBuffer))])) (export-api - #?@(:clj [jvm/parse-qname jvm/make-qname] - :cljs [jsn/parse-qname jsn/make-qname])) + #?@(:clj [jvm/parse-qname jvm/make-qname jvm/encode-uri jvm/decode-uri] + :cljs [jsn/parse-qname jsn/make-qname jsn/encode-uri jsn/decode-uri])) ;; protocol functions can be redefined by extend-*, so we wrap ;; protocols/qname-uri protocols/qname-local within regular fns @@ -51,99 +51,70 @@ (keyword? ns) (name ns) :else (str ns))) -;; # Handling of xmlns - cljns bindings - -(def ^:private nss (atom {:ns->xs {} :xs->ns {}})) - -(defn ns-uri - "Look up xmlns uri to keyword namespace" - [ns] - (-> @nss :ns->xs (get ns))) - -(defn uri-ns - "Look up keyword namespace to xmlns uri" - [uri] - (-> @nss :xs->ns (get uri))) +;; xmlns attributes get special treatment, as they are go into metadata and don't contribute to equality +(def xmlns-uri "http://www.w3.org/2000/xmlns/") +;; TODO find out if xml prefixed names need any special treatment too + ; (def xml-uri "http://www.w3.org/XML/1998/namespace") (extend-protocol AsQName Keyword (qname-local [kw] (name kw)) (qname-uri [kw] (if-let [ns (namespace kw)] - (or (ns-uri ns) - (throw (ex-info (str "Unknown xmlns for clj ns: " (pr-str ns)) - {:qname kw}))) + (if (str/starts-with? ns "xmlns.") + (decode-uri (subs ns 6)) + (if (= "xmlns" ns) + xmlns-uri + (throw (ex-info "Keyword ns is not an xmlns. Needs to be in the form :xmlns./" + {:kw kw})))) "")) #?(:clj String :cljs string) (qname-local [s] (qname-local (parse-qname s))) (qname-uri [s] (qname-uri (parse-qname s)))) (defn canonical-name [uri local prefix] - (if (str/blank? uri) - (keyword local) - (let [kw-prefix (uri-ns uri)] - (if (str/blank? kw-prefix) - (make-qname uri local prefix) - (keyword kw-prefix local))))) + (keyword (when-not (str/blank? uri) + (encode-uri (str "xmlns." uri))) + local)) (defn to-qname [n] (make-qname (or (qname-uri n) "") (qname-local n) "")) -(defn- declare-ns* [{:keys [ns->xs xs->ns] :as acc} [ns xmlns & rst :as nss]] - (if (seq nss) - (do (assert (>= (count nss) 2)) - (let [n (clj-ns-name ns)] - (if-let [x' (ns->xs n)] - (if (= xmlns x') - (recur acc rst) - (throw (ex-info (str "Redefining " n) {:old x' :new xmlns}))) - (if-let [n' (xs->ns xmlns)] - (throw (ex-info (str xmlns " already bound to " n') - {:old n' :new n})) - (recur {:ns->xs (assoc ns->xs n xmlns) - :xs->ns (assoc xs->ns xmlns n)} - rst))))) - acc)) - -(defn declare-ns - "Define mappings in the global keyword-ns -> qname-uri mapping table. - Arguments are pairs of ns-name - qname-uri - ns-name must be a string, symbol, keyword or clojure namespace. The canonical form is string. - ns-uri must be a string" - {:arglists '([& {:as cljns-xmlnss}])} - [& ns-xmlnss] - (swap! nss declare-ns* ns-xmlnss)) - -(declare-ns - :xml "http://www.w3.org/XML/1998/namespace" - :xmlns "http://www.w3.org/2000/xmlns/") - -(def ;;once ^:const - empty-namespace - {"xml" (ns-uri "xml") - "xmlns" (ns-uri "xmlns")}) - -(def ;;once ^:const - xmlns-uri (ns-uri "xmlns")) - -#?(:clj - (defn alias-ns - "Define a clojure namespace alias for shortened keyword and symbol namespaces. +#_(#?(:clj + (defn alias-ns + "Define a clojure namespace alias for shortened keyword and symbol namespaces. Similar to clojure.core/alias, but if namespace doesn't exist, it is created. ## Example (declare-ns :xml.dav \"DAV:\") (alias-ns :D :xml.dav) {:tag ::D/propfind :content []}" + {:arglists '([& {:as alias-nss}])} + [& ans] + (loop [[a n & rst :as ans] ans] + (when (seq ans) + (assert (<= 2 (count ans)) (pr-str ans)) + (let [ns (symbol (clj-ns-name n)) + al (symbol (clj-ns-name a))] + (create-ns ns) + (alias al ns) + (recur rst))))))) + +#?(:clj + (defn alias-uri + "Define a clojure namespace alias for xmlns uri. + ## Example + (alias-uri :D \"DAV:\") + {:tag ::D/propfind}" {:arglists '([& {:as alias-nss}])} [& ans] (loop [[a n & rst :as ans] ans] (when (seq ans) - (assert (<= 2 (count ans)) (pr-str ans)) - (let [ns (symbol (clj-ns-name n)) + (assert (<= (count ans)) (pr-str ans)) + (let [xn (symbol (encode-uri (str "xmlns." n))) al (symbol (clj-ns-name a))] - (create-ns ns) - (alias al ns) + (create-ns xn) + (alias al xn) (recur rst)))))) (defn merge-nss diff --git a/src/main/clojure/xmlns/http%3A%2F%2Fwww/w3/org%2F2000%2Fxmlns%2F.cljc b/src/main/clojure/xmlns/http%3A%2F%2Fwww/w3/org%2F2000%2Fxmlns%2F.cljc new file mode 100644 index 0000000..8277381 --- /dev/null +++ b/src/main/clojure/xmlns/http%3A%2F%2Fwww/w3/org%2F2000%2Fxmlns%2F.cljc @@ -0,0 +1,7 @@ +(ns xmlns.http%3A%2F%2Fwww.w3.org%2F2000%2Fxmlns%2F + "Require - able uri namespace for the `xmlns` prefix. + This uri is special, in that it is predefined in every xml + vocabulary and has the prefix `xmlns` reserved for it, which cannot + be redefined" + {:fixed-prefix "xmlns"}) + diff --git a/src/main/clojure/xmlns/http%3A%2F%2Fwww/w3/org%2FXML%2F1998%2Fnamespace.cljc b/src/main/clojure/xmlns/http%3A%2F%2Fwww/w3/org%2FXML%2F1998%2Fnamespace.cljc new file mode 100644 index 0000000..d1036bf --- /dev/null +++ b/src/main/clojure/xmlns/http%3A%2F%2Fwww/w3/org%2FXML%2F1998%2Fnamespace.cljc @@ -0,0 +1,7 @@ +(ns xmlns.http%3A%2F%2Fwww.w3.org%2FXML%2F1998%2Fnamespace + "Require - able uri namespace for the `xml` prefix. + This uri is special, in that it is predefined in every xml + vocabulary and has the prefix `xml` reserved for it, which cannot + be redefined" + {:fixed-prefix "xml"}) + diff --git a/src/test/clojure/clojure/data/xml/test_emit.clj b/src/test/clojure/clojure/data/xml/test_emit.clj index 65053ab..48ebdce 100644 --- a/src/test/clojure/clojure/data/xml/test_emit.clj +++ b/src/test/clojure/clojure/data/xml/test_emit.clj @@ -41,10 +41,10 @@ (let [expect (str "done")] (is (= expect (emit-str (element "{DAV:}bar" {"{DAV:}item" 1 :xmlns/D "DAV:"} - [(element "{DAV:}baz" {:xml.dav/item 2} "done")])))) + [(element "{DAV:}baz" {:xmlns.DAV%3A/item 2} "done")])))) (is (= expect (emit-str {:tag "{DAV:}bar" :attrs {"{DAV:}item" 1 :xmlns/D "DAV:"} - :content [{:tag "{DAV:}baz" :attrs {:xml.dav/item 2} :content "done"}]})))))) + :content [{:tag "{DAV:}baz" :attrs {:xmlns.DAV%3A/item 2} :content "done"}]})))))) (deftest mixed-quotes (is (= (lazy-parse* @@ -180,9 +180,9 @@ (emit-str (event-seq (java.io.StringReader. "123") {}))))) (deftest test-sibling-xmlns - (let [el (element (to-qname "{NS1}top") {} - (element (to-qname "{NS2}foo")) - (element (to-qname "{NS2}bar")))] + (let [el (element (canonical-name "{NS1}top") {} + (element (canonical-name "{NS2}foo")) + (element (canonical-name "{NS2}bar")))] (is (= (parse-str (emit-str el)) el)))) (deftest test-default-xmlns diff --git a/src/test/clojure/clojure/data/xml/test_names.clj b/src/test/clojure/clojure/data/xml/test_names.clj index 3be312d..ed241fb 100644 --- a/src/test/clojure/clojure/data/xml/test_names.clj +++ b/src/test/clojure/clojure/data/xml/test_names.clj @@ -2,29 +2,21 @@ (:require [clojure.data.xml :refer :all] [clojure.test :refer :all])) -(declare-ns - :xml.dav "DAV:" - :test.xmlns.u "uri-u:" - "test.xmlns.v" "uri-v:" - 'test.xmlns.w "uri-w:" - *ns* "uri-t:") - -(alias-ns - :U :test.xmlns.u - 'V 'test.xmlns.v - "W" "test.xmlns.w" - :D :xml.dav - :T *ns*) +(alias-uri + :U "uri-u:" + :D "DAV:" + 'V "uri-v:" + "W" "uri-w:") (deftest test-types (are [vals values] (every? true? (for [v values] (is (= vals [(qname-uri v) (qname-local v)]) (str "Interpreted QName: " (pr-str v))))) ["" "name"] ["name" :name (parse-qname "name")] - ["uri-u:" "name"] [:test.xmlns.u/name ::U/name "{uri-u:}name" (parse-qname "{uri-u:}name")] - ["uri-v:" "vname"] [:test.xmlns.v/vname ::V/vname "{uri-v:}vname" (parse-qname "{uri-v:}vname")] - ["uri-w:" "wname"] [:test.xmlns.w/wname ::W/wname "{uri-w:}wname" (parse-qname "{uri-w:}wname")] - ["http://www.w3.org/XML/1998/namespace" "name"] [:xml/name] + ["uri-u:" "name"] [::U/name "{uri-u:}name" (parse-qname "{uri-u:}name") (canonical-name "{uri-u:}name")] + ["uri-v:" "vname"] [::V/vname "{uri-v:}vname" (parse-qname "{uri-v:}vname")] + ["uri-w:" "wname"] [::W/wname "{uri-w:}wname" (parse-qname "{uri-w:}wname")] + ;; ["http://www.w3.org/XML/1998/namespace" "name"] [:xml/name] ["http://www.w3.org/2000/xmlns/" "name"] [:xmlns/name])) From 152c10183c677a23da61415f9ec860409cf418eb Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Fri, 30 Sep 2016 12:42:24 +0200 Subject: [PATCH 111/237] expose uri-symbol function and add reader-tag remove dead code --- project.clj | 2 +- src/main/clojure/clojure/data/xml.clj | 4 ++-- src/main/clojure/clojure/data/xml.cljs | 1 - src/main/clojure/clojure/data/xml/name.cljc | 25 ++++----------------- src/main/clojure/data_readers.clj | 1 + 5 files changed, 8 insertions(+), 25 deletions(-) create mode 100644 src/main/clojure/data_readers.clj diff --git a/project.clj b/project.clj index 7f34dac..8ebc9c7 100644 --- a/project.clj +++ b/project.clj @@ -1,4 +1,4 @@ -(defproject org.clojure/data.xml "0-DEVELOPMENT" +(defproject org.clojure/data.xml "0-UE-DEVELOPMENT" :source-paths ["src/main/clojure"] :test-paths ["src/test/clojure"] :resource-paths ["src/test/resources"] diff --git a/src/main/clojure/clojure/data/xml.clj b/src/main/clojure/clojure/data/xml.clj index 09f29f5..eb40695 100644 --- a/src/main/clojure/clojure/data/xml.clj +++ b/src/main/clojure/clojure/data/xml.clj @@ -33,8 +33,8 @@ (export-api node/element* node/element node/cdata node/xml-comment prxml/sexp-as-element prxml/sexps-as-fragment event/element-nss - #_name/ns-uri #_name/uri-ns #_name/declare-ns #_name/alias-ns name/alias-uri - name/parse-qname name/qname-uri name/qname-local name/qname name/to-qname + name/alias-uri name/parse-qname name/qname-uri + name/qname-local name/qname name/to-qname name/uri-symbol process/find-xmlns process/aggregate-xmlns) (defn canonical-name diff --git a/src/main/clojure/clojure/data/xml.cljs b/src/main/clojure/clojure/data/xml.cljs index 54c0c3e..881d5ff 100644 --- a/src/main/clojure/clojure/data/xml.cljs +++ b/src/main/clojure/clojure/data/xml.cljs @@ -7,7 +7,6 @@ [clojure.data.xml.protocols :refer [AsQName]])) (export-api - name/ns-uri name/uri-ns name/declare-ns name/parse-qname name/qname-uri name/qname-local name/qname name/to-qname node/element* node/element node/cdata node/xml-comment) diff --git a/src/main/clojure/clojure/data/xml/name.cljc b/src/main/clojure/clojure/data/xml/name.cljc index eaba822..5440321 100644 --- a/src/main/clojure/clojure/data/xml/name.cljc +++ b/src/main/clojure/clojure/data/xml/name.cljc @@ -27,6 +27,9 @@ ;; protocol functions can be redefined by extend-*, so we wrap ;; protocols/qname-uri protocols/qname-local within regular fns +(defn uri-symbol [uri] + (symbol (encode-uri (str "xmlns." uri)))) + (defn qname-uri "Get the namespace uri for this qname" [v] @@ -80,26 +83,6 @@ (defn to-qname [n] (make-qname (or (qname-uri n) "") (qname-local n) "")) -#_(#?(:clj - (defn alias-ns - "Define a clojure namespace alias for shortened keyword and symbol namespaces. - Similar to clojure.core/alias, but if namespace doesn't exist, it is created. - - ## Example - (declare-ns :xml.dav \"DAV:\") - (alias-ns :D :xml.dav) - {:tag ::D/propfind :content []}" - {:arglists '([& {:as alias-nss}])} - [& ans] - (loop [[a n & rst :as ans] ans] - (when (seq ans) - (assert (<= 2 (count ans)) (pr-str ans)) - (let [ns (symbol (clj-ns-name n)) - al (symbol (clj-ns-name a))] - (create-ns ns) - (alias al ns) - (recur rst))))))) - #?(:clj (defn alias-uri "Define a clojure namespace alias for xmlns uri. @@ -111,7 +94,7 @@ (loop [[a n & rst :as ans] ans] (when (seq ans) (assert (<= (count ans)) (pr-str ans)) - (let [xn (symbol (encode-uri (str "xmlns." n))) + (let [xn (uri-symbol n) al (symbol (clj-ns-name a))] (create-ns xn) (alias al xn) diff --git a/src/main/clojure/data_readers.clj b/src/main/clojure/data_readers.clj new file mode 100644 index 0000000..b48496b --- /dev/null +++ b/src/main/clojure/data_readers.clj @@ -0,0 +1 @@ +{xml/ns clojure.data.xml.name/uri-symbol} From 88fa0a2b4f958419027f9797d965763fdaf0d931 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Mon, 28 Nov 2016 18:49:21 +0100 Subject: [PATCH 112/237] use plugin to get compatibility to clj 1.5 --- pom.xml | 28 +++++++++++++++++++++ src/main/clojure/clojure/data/xml/name.cljc | 2 +- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index aca7914..9a50f21 100644 --- a/pom.xml +++ b/pom.xml @@ -35,6 +35,34 @@ 0.1.2
+ + + + net.bendlas + cljc-maven-plugin + 0.1.0-SNAPSHOT + + + generate-sources + split + + + + + + + + 1.5.0 + + + + + + clojars.org + https://clojars.org/repo + + + scm:git:git@github.com:clojure/data.xml.git scm:git:git@github.com:clojure/data.xml.git diff --git a/src/main/clojure/clojure/data/xml/name.cljc b/src/main/clojure/clojure/data/xml/name.cljc index 5440321..9aaa6e8 100644 --- a/src/main/clojure/clojure/data/xml/name.cljc +++ b/src/main/clojure/clojure/data/xml/name.cljc @@ -64,7 +64,7 @@ (qname-local [kw] (name kw)) (qname-uri [kw] (if-let [ns (namespace kw)] - (if (str/starts-with? ns "xmlns.") + (if (.startsWith ns "xmlns.") (decode-uri (subs ns 6)) (if (= "xmlns" ns) xmlns-uri From 8ab647ed2dd4540e69dc282fdd013a5b5abdb1f0 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Wed, 30 Nov 2016 02:48:55 +0100 Subject: [PATCH 113/237] Prepare documents for 0.2.0-alpha1 --- CHANGES.md | 7 ++++ README.md | 103 ++++++++++++++--------------------------------------- 2 files changed, 34 insertions(+), 76 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 2ceef9a..f9ab071 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,10 @@ +From 0.1.0-beta3 to 0.2.0-alpha1 +- Define uniform mapping of xml namespaces to clojure namespaces via percent-encoding +- Remove declare-ns and alias-ns +- Introduce alias-uri +- Clojurescript support +- data.xml now requires Clojure 1.5.0+ (due to percent-sign in keywords) + From 0.1.0-beta2 to 0.1.0-beta3 - Fix emitter to keep non-namespaced xml names out of any set default namespace - Add support for location info in parser diff --git a/README.md b/README.md index 402dbd6..07737d7 100644 --- a/README.md +++ b/README.md @@ -37,9 +37,9 @@ Add the following to the `project.clj` dependencies: [org.clojure/data.xml "0.0.8"] -## Installation - Beta +## Installation - Alpha -Latest beta release: 0.1.0-beta3 +Latest alpha release: 0.2.0-alpha1 * [All Released Versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22data.xml%22) @@ -51,13 +51,13 @@ For Maven projects, add the following XML in your `pom.xml`'s `` s org.clojure data.xml - 0.1.0-beta3 + 0.2.0-alpha1 ### Leiningen Add the following to the `project.clj` dependencies: - [org.clojure/data.xml "0.1.0-beta3"] + [org.clojure/data.xml "0.2.0-alpha1"] ## Examples @@ -91,7 +91,7 @@ can be passed via key pairs: #clojure.data.xml.Element{:tag :a, :attrs {}, :content ("\nfoo bar\n" "\nbaz\n")} XML elements can be created using the typical defrecord constructor -functions or the element function used below, and written using a +functions or the element function used below or just a plain map with :tag :attrs :content keys, and written using a [java.io.Writer](http://docs.oracle.com/javase/6/docs/api/java/io/Writer.html).: (let [tags (element :foo {:foo-attr "foo value"} @@ -196,84 +196,36 @@ Generated API docs for data.xml are available [here](http://clojure.github.com/d ## Namespace Support -Parsing and emitting XML namespaces are supported and use the JDK built-in -QName class. Below is an example of parsing an XHTML document: +XML Namespaced names (QNames) are commonly encoded into clojure keywords, by percent-encoding the (XML) namespace: `{http://www.w3.org/1999/xhtml}head` is encoded in data.xml as `:http%3A%2F%2Fwww.w3.org%2F1999%2Fxhtml/head`. + +Below is an example of parsing an XHTML document: (parse-str " - - - Example title - - - Example Paragraph - - ") - - #...Element{:tag #object[javax.xml.namespace.QName 0x68651690 "{http://www.w3.org/1999/xhtml}html"], - :attrs {}, - :content (...)} - -The above data structures are verbose. Each tag that includes a -namespace will include that in it's QName: - - #...Element{:tag #object[javax.xml.namespace.QName 0x7255cde4 "{http://www.w3.org/1999/xhtml}title"], + ") + + #...Element{:tag :xmlns.http%3A%2F%2Fwww.w3.org%2F1999%2Fxhtml/html, :attrs {}, - :content ("Example title")} - -This is the most basic representation of the parsed document that -includes namespaces. Emitting namespace information in a similar way -can use the `qname` function: - - (element (qname "title" "http://www.w3.org/1999/xhtml" "foo") - {} - "Example title") + :content ()} - #...Element{:tag #object[javax.xml.namespace.QName 0x22a22c0e "{title}http://www.w3.org/1999/xhtml"], - :attrs {}, - :content ("Example title")} +Emitting namespaced XML is usually done by using `alias-uri` in combination with clojure's built-in `::kw-ns/shorthands`: -The emitting code above is similarly verbose. By declaring the -namespaces that will be parsed or emitted up-front via `declare-ns`, -these representations can be made much more succinct: + (alias-uri 'xh "http://www.w3.org/1999/xhtml") + + (emit-str {:tag ::xh/html + :content [{:tag ::xh/head} {:tag ::xh/body :content ["DOCUMENT"]}]}) - (declare-ns "xml.html" "http://www.w3.org/1999/xhtml") - (parse-str " - - - Example title - - - Example Paragraph - - ") - - #...Element{:tag :xml.html/html, :attrs {}, :content (...)} - -In the above example, all tags use the namespace -`http://www.w3.org/1999/xhtml`. That namespace is declared as "xml.html" in -Clojure. All the tags parsed from that document will be -`:xml.html/the-tag-name`. Note that `xml.html` is not related to the namespace -prefix declared in the document (`foo` in this example). `xml.html` is just -a way to refer to names in the `http://www.w3.org/1999/xhtml` namespace -with a keyword. - -The declared namespace can also be used in combination with the -regular clojure namespace aliasing mechnism. When constructing XML -documents, this leads pretty succinct representation with alias-aware keywords. - - (alias-ns :xh :xml.html) ;; alias-ns will create the target ns - xml.html - so that it can be aliased into the current ns - (emit-str (element ::xh/title - {:xmlns/foo "http://www.w3.org/1999/xhtml"} - "Example title")) - - "Example title" + + + + DOCUMENT + -Take note, that the keyword-namespaces `:xmlns/...` as well as -`:xml/...` are predefined to refer to `http://www.w3.org/2000/xmlns/` -and `http://www.w3.org/XML/1998/namespace` respectively. +It is also allowable to use `javax.xml.namespace.QName` instances, as well as strings with the informal `{ns}n` encoding. -Because keywords interact with clojure's namespace - aliasing -mechanism, applications can choose descriptive names in `declare-ns`. + (emit-str {:tag (qname "http://www.w3.org/1999/xhtml" "html")}) + (emit-str {:tag "{http://www.w3.org/1999/xhtml}html"}) + + ### Namespace Prefixes @@ -288,7 +240,6 @@ kw-namespace: Not specifying a namespace prefix will results in a prefix being generated: - ;; Assumes (declare-ns "xml.html" "http://www.w3.org/1999/xhtml") and (alias-ns :xh :xml.html) (emit-str (element ::xh/title {} "Example title")) From 78e36db9729c3a0f7ad325f9262fb7cc639b540c Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Fri, 2 Dec 2016 18:44:29 +0100 Subject: [PATCH 114/237] Implement beginning of cljs test suite Multiple fixes: - data extended dom now provides equality - Provide reification to native nodes or to maps - include a mock xml dom for tests in nashorn - unfortunately, this can't test native dom extension, since xmldom is spotty --- .gitignore | 6 + README.md | 33 + dxml-nashorn.global.js | 8 + package.json | 13 + pom.xml | 20 + project.clj | 9 +- src/main/clojure/clojure/data/xml.cljs | 86 +- src/main/clojure/clojure/data/xml/node.cljs | 172 +- .../clojure/clojure/data/xml/test_cljs.clj | 77 + .../clojure/clojure/data/xml/test_cljs.cljs | 11 + .../clojure/data/xml/test_cljs_basic.cljs | 25 + .../clojure/data/xml/test_cljs_extended.cljs | 10 + src/test/resources/dxml-nashorn.generated.js | 2254 +++++++++++++++++ .../resources/public/cljs-tests/index.html | 5 + webpack.config.js | 7 + 15 files changed, 2654 insertions(+), 82 deletions(-) create mode 100644 dxml-nashorn.global.js create mode 100644 package.json create mode 100644 src/test/clojure/clojure/data/xml/test_cljs.clj create mode 100644 src/test/clojure/clojure/data/xml/test_cljs.cljs create mode 100644 src/test/clojure/clojure/data/xml/test_cljs_basic.cljs create mode 100644 src/test/clojure/clojure/data/xml/test_cljs_extended.cljs create mode 100644 src/test/resources/dxml-nashorn.generated.js create mode 100644 src/test/resources/public/cljs-tests/index.html create mode 100644 webpack.config.js diff --git a/.gitignore b/.gitignore index 5cdab64..e3436a0 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,9 @@ modules/xml/target/ modules/xml.pull-parser/target/ *~ +/out/ +/node_modules/ +/nashorn_code_cache/ +/figwheel_server.log +/.nrepl-port +/.cljs_nashorn_repl/ diff --git a/README.md b/README.md index 07737d7..7cc85e5 100644 --- a/README.md +++ b/README.md @@ -289,6 +289,39 @@ To elide location information, pass `:location-info false` to the parser: (parse-str your-input :location-info false) +## Clojurescript support + +The Clojurescript implementation uses the same namespace as the Clojure one `clojure.data.xml`. + +### Differences from Clojure implementation + +data.xml uses native browser dom elements to represent xml. To get back the convenience of treating them as maps, call `(extend-dom-as-data!)`. This extends the native dom node prototypes to Clojurescript collection protocols, such that you can treat them as data.xml parse trees. + +Of course, the map format is supported for emitting. + +### Missing Features, Patches Welcome + +#### Streaming + +data.xml on Clojurescript doesn't currently support streaming, hence only the `*-str` variants of `parse`/`emit` are implemented. Those are just wrappers for browser's native xml parsing/printing. + +Pull parsing doesn't seem the right solution for Clojurescript, because when code cannot block, the parser has no way of waiting on its input. For this reason, parsing in Clojurescript cannot be based around `event-seq`. + +Push parsing, on the other hand should not pose a problem, because when data arrives in a callback, it can be pushed on into the parser. Fortunately, clojure already has a nice push-based pendant for lazy sequences: transducers. + +#### Utilities + +Some utilities, like `process/*-xmlns`, `prxml/sexp-as-*`, `indent` aren't yet implemented. + +#### Immutable updates for dom types + +Make `extend-dom-as-data!` also support assoc, ... on dom nodes. + +#### Data coercions + + + + ## License Licensed under the [Eclipse Public License](http://www.opensource.org/licenses/eclipse-1.0.php). diff --git a/dxml-nashorn.global.js b/dxml-nashorn.global.js new file mode 100644 index 0000000..f983484 --- /dev/null +++ b/dxml-nashorn.global.js @@ -0,0 +1,8 @@ +global.console = { + log: print, + warn: print, + error: print +}; +global.xmldom = require("xmldom"); +global.DOMParser = global.xmldom.DOMParser; +global.XMLSerializer = global.xmldom.XMLSerializer; diff --git a/package.json b/package.json new file mode 100644 index 0000000..a59f89b --- /dev/null +++ b/package.json @@ -0,0 +1,13 @@ +{ + + "scripts": { + "build": "webpack" + } + + , "devDependencies": { + "webpack": "2.1.0-beta.27", + "xmldom" : "0.1.27" + "domino" : "" + } + , "name": "dxml-nashorn-api" +} diff --git a/pom.xml b/pom.xml index 9a50f21..72d57a6 100644 --- a/pom.xml +++ b/pom.xml @@ -48,6 +48,26 @@ + + maven-jar-plugin + 2.4 + + + default-jar + package + + jar + + + + **/*.clj + **/*.cljs + **/*.cljc + + + + + diff --git a/project.clj b/project.clj index 8ebc9c7..a34728b 100644 --- a/project.clj +++ b/project.clj @@ -1,5 +1,10 @@ (defproject org.clojure/data.xml "0-UE-DEVELOPMENT" :source-paths ["src/main/clojure"] :test-paths ["src/test/clojure"] - :resource-paths ["src/test/resources"] - :dependencies [[org.clojure/clojure "1.8.0"]]) + :resource-paths ["src/test/resources" "target/gen-resources"] + :dependencies [[org.clojure/clojure "1.8.0"] + [org.clojure/clojurescript "1.9.293"] + [com.cemerick/piggieback "0.2.1"] + [org.clojure/tools.nrepl "0.2.12"] + [figwheel-sidecar "0.5.8"]] + :repl-options {:nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]}) diff --git a/src/main/clojure/clojure/data/xml.cljs b/src/main/clojure/clojure/data/xml.cljs index 881d5ff..91e5bcc 100644 --- a/src/main/clojure/clojure/data/xml.cljs +++ b/src/main/clojure/clojure/data/xml.cljs @@ -7,30 +7,37 @@ [clojure.data.xml.protocols :refer [AsQName]])) (export-api - name/parse-qname name/qname-uri name/qname-local name/qname name/to-qname - node/element* node/element node/cdata node/xml-comment) + name/parse-qname name/qname-uri name/qname-local name/qname name/to-qname name/uri-symbol + node/element* node/element node/cdata node/xml-comment node/text-node + node/extend-dom-as-data! node/element-node node/element-data) (defn canonical-name "Put (q)name into canonical form as per ns-env" [n] (name/canonical-name (qname-uri n) (qname-local n) "")) -;; TODO event-seq +;;;; ## TODO event-seq +;; This probably won't happen due to js' non-blocking semantics +;; Instead, for clojurescript, the machinery around event-seq could be implemented +;; as a transducer stack, such that a push-based source for parser events, like sax-js, +;; could be used. + ;; TODO parse (use goog StringBuffer?) (defn parse-str "Use DOMParser to parse xml string" ;; TODO detect browser specific parsererror tags ;; see http://stackoverflow.com/questions/11563554/how-do-i-detect-xml-parsing-errors-when-using-javascripts-domparser-in-a-cross - [s & {:keys [content-type on-error] + [s & {:keys [content-type on-error raw] :or {content-type "text/xml" on-error #(throw "XML parser error" {:doc % :input s})}}] (let [dom (. (js/DOMParser.) (parseFromString s content-type)) doc (.-documentElement dom)] - (if (= "parsererror" (.-nodeName doc)) - (on-error doc) - doc))) + (cond (= "parsererror" (.-nodeName doc)) + (on-error doc) + raw doc + :else (element-data doc)))) ;; TODO emit (use goog StringBuffer?) @@ -39,68 +46,5 @@ [e & {:keys []}] (. (js/XMLSerializer.) (serializeToString - (node/coerce-to-dom e)))) - -(defn extend-nodes-as-qname! [] - (extend-protocol AsQName - js/Element - (qname-local [e] (.-localName e)) - (qname-uri [e] (.-namespaceURI e)) - js/Attr - (qname-local [e] (.-localName e)) - (qname-uri [e] (.-namespaceURI e)))) - -(defn- as-node [n] - (if (instance? js/Text n) - (.-wholeText n) ;; .-data - n)) - -(defn extend-dom-as-data! [] - (extend-type js/Element - ILookup - (-lookup [el k] - (case k - :tag (name/canonical-name (.-namespaceURI el) - (.-localName el) - "") - :attrs (.-attributes el) - :content (.-childNodes el) - (throw "XML tag has no key" {:key k :el el})))) - (extend-type js/NamedNodeMap - ILookup - (-lookup - ([attrs attr] - (if-let [i (.getNamedItemNS attrs (qname-uri attr) (qname-local attr))] - (.-value i) - nil)) - ([attrs attr not-found] - (if-let [i (.getNamedItemNS attrs (qname-uri attr) (qname-local attr))] - (.-value i) - not-found)))) - (extend-type js/NodeList - ISeqable - (-seq [nl] (map as-node (array-seq nl))) - ISequential - ICounted - (-count [nl] (.-length nl)) - IIndexed - (-nth - ([nl n] - (as-node (aget nl n))) - ([nl n nf] - (if (and (<= 0 n) (< n (.-length nl))) - (as-node (aget nl n)) - nf))))) - -(comment - - (extend-dom-as-data!) - (extend-nodes-as-qname!) + (node/element-node e)))) - (let [{tag :tag {a1 "{DAV:}a1" a2 "a2" :as attrs} :attrs [c1 c2 c3] :content} - (parse-str " -Fancy Content
More -
")] - [tag a1 a2 c1 c2 c3]) - - ) diff --git a/src/main/clojure/clojure/data/xml/node.cljs b/src/main/clojure/clojure/data/xml/node.cljs index e038699..eb7317f 100644 --- a/src/main/clojure/clojure/data/xml/node.cljs +++ b/src/main/clojure/clojure/data/xml/node.cljs @@ -1,11 +1,16 @@ (ns clojure.data.xml.node (:require - [clojure.data.xml.name :refer [qname-uri qname-local]])) + [clojure.data.xml.name :refer [qname-uri qname-local canonical-name]])) (def doc (.. (js/DOMParser.) (parseFromString "" "text/xml"))) +(defn text-node + "Create a Text node" + [s] + (.createTextNode doc s)) + (defn element* "Create an xml element from a content collection and optional metadata" ([tag attrs content meta] @@ -32,7 +37,7 @@ nil attrs) (reduce (fn [_ n] (.appendChild el (if (string? n) - (.createTextNode doc n) + (text-node n) n))) nil content) el))) @@ -53,11 +58,160 @@ [content] (.createComment doc content)) -(defn coerce-to-dom [node] +(declare element-node) + +(defn node-list + "Create a NodeList" + [elements] + (let [f (.createDocumentFragment doc)] + (doseq [el elements] + (.appendChild f (element-node el))) + (.-childNodes f))) + +;; ## Types + +;; we get these from reflection, to only depend only on js/DOMParser and js/XMLSerializer +;; these can easily be provided in nashorn, ... + +(def Text (type (text-node ""))) +(def Element (type (element :e))) +(def NamedNodeMap (type (.-attributes (element :e)))) +(def NodeList (type (node-list []))) + +;; ## Coercions + +;; ## -> DOM + +(defn element-node + "Coerce xml elements to dom nodes" + [el] (cond - (string? node) node - (instance? js/Element node) node - (satisfies? ILookup node) (element* (:tag node) - (:attrs node) - (map coerce-to-dom (:content node))) - :else (throw (ex-info "Cannot coerce" {:form node})))) + (string? el) (text-node el) + (instance? Element el) el + ;; stupid xmldom, (some? (.-item el)) + (instance? NodeList el) el + (instance? Text el) el + (satisfies? ILookup el) (element* (:tag el) + (:attrs el) + (map element-node (:content el))) + (satisfies? ISequential el) (node-list el) + :else (throw (ex-info "Cannot coerce" {:form el})))) + +;; ## -> DATA + +(defn- dom-element-tag [el] + (canonical-name (.-namespaceURI el) + (.-localName el) + "")) + +(defn dom-element-attrs [el] + (persistent! + (reduce (fn [ta attr-node] + (assoc! ta + (dom-element-tag attr-node) + (.-value attr-node))) + (transient {}) + (array-seq el)))) + +(declare element-data) + +(defn- node-list-vec [nl] + (mapv element-data (array-seq nl))) + +(defn- as-node [n] + (if (instance? Text n) + (.-nodeValue n) ;; .-data + n)) + +(defn element-data + "Coerce xml elements to element maps / content vectors" + [el] + (cond + (instance? Text el) + (.-nodeValue el) + (instance? Element el) + {:tag (dom-element-tag el) + :attrs (dom-element-attrs (.-attributes el)) + :content (node-list-vec (.-childNodes el))} + ;;(instance? NamedNodeMap el) + (.-getNamedItemNS el) + (dom-element-attrs el) + (instance? NodeList el) (node-list-vec el) + (string? el) el + (satisfies? ILookup el) el + (satisfies? ISequential el) el + :else (throw (ex-info "Element cannot be converted to data" {:element el})))) + +(defn extend-dom-as-data! [] + (extend-type Element + ILookup + (-lookup [el k] + (case k + :tag (dom-element-tag el) + :attrs (.-attributes el) + :content (.-childNodes el) + (throw "XML tag has no key" {:key k :el el}))) + IEquiv + (-equiv [el0 el1] + (.isEqualNode el0 el1))) + (extend-type NamedNodeMap + ILookup + (-lookup + ([attrs attr] + (if-let [i (.getNamedItemNS attrs (qname-uri attr) (qname-local attr))] + (.-value i) + nil)) + ([attrs attr not-found] + (if-let [i (.getNamedItemNS attrs (qname-uri attr) (qname-local attr))] + (.-value i) + not-found))) + ICounted + (-count [nm] (alength nm)) + IKVReduce + (-kv-reduce [nm f init] + (reduce (fn [acc attr] + (f acc (dom-element-tag attr) (.-value attr))) + init nm)) + IIndexed + (-nth + ([nm n] (.-value (aget nm n))) + ([nm n nf] (if (and (<= 0 n) (< n (alength nm))) + (.-value (aget nm n)) + nf))) + IEquiv + (-equiv [nm0 nm1] + (and (identical? (count nm0) (count nm1)) + (reduce-kv (fn [_ qn v] + (or (identical? v (get nm1 qn "")) + (reduced false))) + true nm0)))) + (extend-type NodeList + ;specify! (.. (node-list []) -constructor -prototype) + ISeqable + (-seq [nl] (map as-node (array-seq nl))) + ISequential + ICounted + (-count [nl] (alength nl)) + IIndexed + (-nth + ([nl n] + (as-node (aget nl n))) + ([nl n nf] + (if (and (<= 0 n) (< n (alength nl))) + (as-node (aget nl n)) + nf))) + IEquiv + (-equiv [nl0 nl1] + (and (identical? (count nl0) (count nl1)) + (reduce (fn [_ n] + (or (= (nth nl0 n) (nth nl1 n)) + (reduced false))) + true (range (count nl0)))))) + (extend-type Text + IEquiv + (-equiv [t0 t1] + (identical? (.-nodeValue t0) (.-nodeValue t1)))) + {'Text Text + 'Element Element + 'NamedNodeMap NamedNodeMap + 'NodeList NodeList}) diff --git a/src/test/clojure/clojure/data/xml/test_cljs.clj b/src/test/clojure/clojure/data/xml/test_cljs.clj new file mode 100644 index 0000000..c91a239 --- /dev/null +++ b/src/test/clojure/clojure/data/xml/test_cljs.clj @@ -0,0 +1,77 @@ +; Copyright (c) Rich Hickey. 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 ^{:doc "Clojurescript tests for data.xml"} + clojure.data.xml.test-cljs + (:require + [clojure.test :refer :all] + [cljs.repl :as repl] + [cljs.repl.nashorn :as repl-nh] + [cemerick.piggieback :as pback] + [cljs.closure :as closure] + #_[cljs.analyzer :as ana] + [cljs.build.api :as bapi] + [figwheel-sidecar.repl-api :refer [start-figwheel! stop-figwheel! cljs-repl]]) + (:import + java.nio.file.Files + java.nio.file.attribute.FileAttribute)) + +(defn nashorn-env [] + (let [{:as env :keys [engine]} (repl-nh/repl-env)] + (repl-nh/eval-resource engine "dxml-nashorn.generated.js" true) + env)) + +(def handle-redirect (constantly {:status 307 :headers {"Location" "/cljs-tests/index.html"}})) + +(defn repl-figwheel! [] + (start-figwheel! + {:figwheel-options + {:http-server-root "public" + :ring-handler `handle-redirect} + :all-builds + [{:id "tests" + :source-paths ["src/main/clojure" "src/test/clojure"] + :figwheel {:on-jsload "clojure.data.xml.test-cljs/-main"} + :compiler {:main 'clojure.data.xml.test-cljs + :output-to "target/gen-resources/public/cljs-tests/main.js" + :output-dir "target/gen-resources/public/cljs-tests/output" + :asset-path "output" + :source-map true}}]}) + (cljs-repl)) + +(defn repl-piggieback! [] + (pback/cljs-repl (nashorn-env))) + +(defn repl-main! [] + (repl/repl (nashorn-env))) + +(defn tempdir [] + (str (Files/createTempDirectory + "cljs-nashorn-" (into-array FileAttribute [])))) + +(defn compile-testsuite [] + (bapi/build (bapi/inputs "src/main/clojure" "src/test/clojure") + (let [dir (tempdir)] + (println "Building in" dir) + {:output-to (str dir "/tests.js") + :output-dir dir + :optimizations :whitespace + :pretty-print true + :preamble ["src/test/resources/dxml-nashorn.generated.js"]}))) + +#_(deftest clojurescript-test-suite + (is (= :success + (read-string + (repl/evaluate-form + (doto (repl-nh/repl-env :debug true) + (repl/-setup {:output-dir (tempdir)})) + (ana/empty-env) + "TESTSUITE LAUNCHER" + (list 'do + (list 'require ''clojure.data.xml.test-cljs) + (list 'clojure.data.xml.test-cljs/run-tests))))))) diff --git a/src/test/clojure/clojure/data/xml/test_cljs.cljs b/src/test/clojure/clojure/data/xml/test_cljs.cljs new file mode 100644 index 0000000..b39f690 --- /dev/null +++ b/src/test/clojure/clojure/data/xml/test_cljs.cljs @@ -0,0 +1,11 @@ +(ns clojure.data.xml.test-cljs + (:require [cljs.test :as test] + [clojure.data.xml :as xml] + clojure.data.xml.test-cljs-basic + clojure.data.xml.test-cljs-extended)) + +(defn ^:export -main [] + (test/run-tests 'clojure.data.xml.test-cljs-basic) + (xml/extend-dom-as-data!) + (test/testing "with extended native dom" + (test/run-tests 'clojure.data.xml.test-cljs-basic 'clojure.data.xml.test-cljs-extended))) diff --git a/src/test/clojure/clojure/data/xml/test_cljs_basic.cljs b/src/test/clojure/clojure/data/xml/test_cljs_basic.cljs new file mode 100644 index 0000000..1e576fd --- /dev/null +++ b/src/test/clojure/clojure/data/xml/test_cljs_basic.cljs @@ -0,0 +1,25 @@ +(ns clojure.data.xml.test-cljs-basic + (:require + [cljs.test :as test :refer [deftest is are]] + [clojure.data.xml :as xml :refer [parse-str emit-str element element-data]] + [clojure.data.xml.node :as node])) + + +(comment + + (-> "" + parse-str + emit-str) + + (-> :foo xml/element + emit-str parse-str) + + + + ) + + +(deftest roundtrips + (are [dxml xml] (do (is (= (element-data dxml) (xml/parse-str xml))) + (is (= (element-data dxml) (xml/parse-str (xml/emit-str dxml))))) + (xml/element :foo) "")) diff --git a/src/test/clojure/clojure/data/xml/test_cljs_extended.cljs b/src/test/clojure/clojure/data/xml/test_cljs_extended.cljs new file mode 100644 index 0000000..f676572 --- /dev/null +++ b/src/test/clojure/clojure/data/xml/test_cljs_extended.cljs @@ -0,0 +1,10 @@ +(ns clojure.data.xml.test-cljs-extended + (:require + [cljs.test :as test :refer [deftest is are]] + [clojure.data.xml :as xml :refer [parse-str emit-str element element-data]] + [clojure.data.xml.node :as node])) + +(deftest roundtrips + (are [dxml xml] (do (is (= dxml (xml/parse-str xml :raw true))) + (is (= dxml (xml/parse-str (xml/emit-str dxml) :raw true)))) + (xml/element :foo) "")) diff --git a/src/test/resources/dxml-nashorn.generated.js b/src/test/resources/dxml-nashorn.generated.js new file mode 100644 index 0000000..0c3ff4a --- /dev/null +++ b/src/test/resources/dxml-nashorn.generated.js @@ -0,0 +1,2254 @@ +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; + +/******/ // The require function +/******/ function __webpack_require__(moduleId) { + +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) +/******/ return installedModules[moduleId].exports; + +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; + +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); + +/******/ // Flag the module as loaded +/******/ module.l = true; + +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } + + +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; + +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; + +/******/ // identity function for calling harmory imports with the correct context +/******/ __webpack_require__.i = function(value) { return value; }; + +/******/ // define getter function for harmory exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ Object.defineProperty(exports, name, { +/******/ configurable: false, +/******/ enumerable: true, +/******/ get: getter +/******/ }); +/******/ }; + +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; + +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; + +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; + +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = 4); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ function(module, exports) { + +/* + * DOM Level 2 + * Object DOMException + * @see http://www.w3.org/TR/REC-DOM-Level-1/ecma-script-language-binding.html + * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/ecma-script-binding.html + */ + +function copy(src,dest){ + for(var p in src){ + dest[p] = src[p]; + } +} +/** +^\w+\.prototype\.([_\w]+)\s*=\s*((?:.*\{\s*?[\r\n][\s\S]*?^})|\S.*?(?=[;\r\n]));? +^\w+\.prototype\.([_\w]+)\s*=\s*(\S.*?(?=[;\r\n]));? + */ +function _extends(Class,Super){ + var pt = Class.prototype; + if(Object.create){ + var ppt = Object.create(Super.prototype) + pt.__proto__ = ppt; + } + if(!(pt instanceof Super)){ + function t(){}; + t.prototype = Super.prototype; + t = new t(); + copy(pt,t); + Class.prototype = pt = t; + } + if(pt.constructor != Class){ + if(typeof Class != 'function'){ + console.error("unknow Class:"+Class) + } + pt.constructor = Class + } +} +var htmlns = 'http://www.w3.org/1999/xhtml' ; +// Node Types +var NodeType = {} +var ELEMENT_NODE = NodeType.ELEMENT_NODE = 1; +var ATTRIBUTE_NODE = NodeType.ATTRIBUTE_NODE = 2; +var TEXT_NODE = NodeType.TEXT_NODE = 3; +var CDATA_SECTION_NODE = NodeType.CDATA_SECTION_NODE = 4; +var ENTITY_REFERENCE_NODE = NodeType.ENTITY_REFERENCE_NODE = 5; +var ENTITY_NODE = NodeType.ENTITY_NODE = 6; +var PROCESSING_INSTRUCTION_NODE = NodeType.PROCESSING_INSTRUCTION_NODE = 7; +var COMMENT_NODE = NodeType.COMMENT_NODE = 8; +var DOCUMENT_NODE = NodeType.DOCUMENT_NODE = 9; +var DOCUMENT_TYPE_NODE = NodeType.DOCUMENT_TYPE_NODE = 10; +var DOCUMENT_FRAGMENT_NODE = NodeType.DOCUMENT_FRAGMENT_NODE = 11; +var NOTATION_NODE = NodeType.NOTATION_NODE = 12; + +// ExceptionCode +var ExceptionCode = {} +var ExceptionMessage = {}; +var INDEX_SIZE_ERR = ExceptionCode.INDEX_SIZE_ERR = ((ExceptionMessage[1]="Index size error"),1); +var DOMSTRING_SIZE_ERR = ExceptionCode.DOMSTRING_SIZE_ERR = ((ExceptionMessage[2]="DOMString size error"),2); +var HIERARCHY_REQUEST_ERR = ExceptionCode.HIERARCHY_REQUEST_ERR = ((ExceptionMessage[3]="Hierarchy request error"),3); +var WRONG_DOCUMENT_ERR = ExceptionCode.WRONG_DOCUMENT_ERR = ((ExceptionMessage[4]="Wrong document"),4); +var INVALID_CHARACTER_ERR = ExceptionCode.INVALID_CHARACTER_ERR = ((ExceptionMessage[5]="Invalid character"),5); +var NO_DATA_ALLOWED_ERR = ExceptionCode.NO_DATA_ALLOWED_ERR = ((ExceptionMessage[6]="No data allowed"),6); +var NO_MODIFICATION_ALLOWED_ERR = ExceptionCode.NO_MODIFICATION_ALLOWED_ERR = ((ExceptionMessage[7]="No modification allowed"),7); +var NOT_FOUND_ERR = ExceptionCode.NOT_FOUND_ERR = ((ExceptionMessage[8]="Not found"),8); +var NOT_SUPPORTED_ERR = ExceptionCode.NOT_SUPPORTED_ERR = ((ExceptionMessage[9]="Not supported"),9); +var INUSE_ATTRIBUTE_ERR = ExceptionCode.INUSE_ATTRIBUTE_ERR = ((ExceptionMessage[10]="Attribute in use"),10); +//level2 +var INVALID_STATE_ERR = ExceptionCode.INVALID_STATE_ERR = ((ExceptionMessage[11]="Invalid state"),11); +var SYNTAX_ERR = ExceptionCode.SYNTAX_ERR = ((ExceptionMessage[12]="Syntax error"),12); +var INVALID_MODIFICATION_ERR = ExceptionCode.INVALID_MODIFICATION_ERR = ((ExceptionMessage[13]="Invalid modification"),13); +var NAMESPACE_ERR = ExceptionCode.NAMESPACE_ERR = ((ExceptionMessage[14]="Invalid namespace"),14); +var INVALID_ACCESS_ERR = ExceptionCode.INVALID_ACCESS_ERR = ((ExceptionMessage[15]="Invalid access"),15); + + +function DOMException(code, message) { + if(message instanceof Error){ + var error = message; + }else{ + error = this; + Error.call(this, ExceptionMessage[code]); + this.message = ExceptionMessage[code]; + if(Error.captureStackTrace) Error.captureStackTrace(this, DOMException); + } + error.code = code; + if(message) this.message = this.message + ": " + message; + return error; +}; +DOMException.prototype = Error.prototype; +copy(ExceptionCode,DOMException) +/** + * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-536297177 + * The NodeList interface provides the abstraction of an ordered collection of nodes, without defining or constraining how this collection is implemented. NodeList objects in the DOM are live. + * The items in the NodeList are accessible via an integral index, starting from 0. + */ +function NodeList() { +}; +NodeList.prototype = { + /** + * The number of nodes in the list. The range of valid child node indices is 0 to length-1 inclusive. + * @standard level1 + */ + length:0, + /** + * Returns the indexth item in the collection. If index is greater than or equal to the number of nodes in the list, this returns null. + * @standard level1 + * @param index unsigned long + * Index into the collection. + * @return Node + * The node at the indexth position in the NodeList, or null if that is not a valid index. + */ + item: function(index) { + return this[index] || null; + }, + toString:function(isHTML,nodeFilter){ + for(var buf = [], i = 0;i=0){ + var lastIndex = list.length-1 + while(i0 || key == 'xmlns'){ +// return null; +// } + //console.log() + var i = this.length; + while(i--){ + var attr = this[i]; + //console.log(attr.nodeName,key) + if(attr.nodeName == key){ + return attr; + } + } + }, + setNamedItem: function(attr) { + var el = attr.ownerElement; + if(el && el!=this._ownerElement){ + throw new DOMException(INUSE_ATTRIBUTE_ERR); + } + var oldAttr = this.getNamedItem(attr.nodeName); + _addNamedNode(this._ownerElement,this,attr,oldAttr); + return oldAttr; + }, + /* returns Node */ + setNamedItemNS: function(attr) {// raises: WRONG_DOCUMENT_ERR,NO_MODIFICATION_ALLOWED_ERR,INUSE_ATTRIBUTE_ERR + var el = attr.ownerElement, oldAttr; + if(el && el!=this._ownerElement){ + throw new DOMException(INUSE_ATTRIBUTE_ERR); + } + oldAttr = this.getNamedItemNS(attr.namespaceURI,attr.localName); + _addNamedNode(this._ownerElement,this,attr,oldAttr); + return oldAttr; + }, + + /* returns Node */ + removeNamedItem: function(key) { + var attr = this.getNamedItem(key); + _removeNamedNode(this._ownerElement,this,attr); + return attr; + + + },// raises: NOT_FOUND_ERR,NO_MODIFICATION_ALLOWED_ERR + + //for level2 + removeNamedItemNS:function(namespaceURI,localName){ + var attr = this.getNamedItemNS(namespaceURI,localName); + _removeNamedNode(this._ownerElement,this,attr); + return attr; + }, + getNamedItemNS: function(namespaceURI, localName) { + var i = this.length; + while(i--){ + var node = this[i]; + if(node.localName == localName && node.namespaceURI == namespaceURI){ + return node; + } + } + return null; + } +}; +/** + * @see http://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-102161490 + */ +function DOMImplementation(/* Object */ features) { + this._features = {}; + if (features) { + for (var feature in features) { + this._features = features[feature]; + } + } +}; + +DOMImplementation.prototype = { + hasFeature: function(/* string */ feature, /* string */ version) { + var versions = this._features[feature.toLowerCase()]; + if (versions && (!version || version in versions)) { + return true; + } else { + return false; + } + }, + // Introduced in DOM Level 2: + createDocument:function(namespaceURI, qualifiedName, doctype){// raises:INVALID_CHARACTER_ERR,NAMESPACE_ERR,WRONG_DOCUMENT_ERR + var doc = new Document(); + doc.implementation = this; + doc.childNodes = new NodeList(); + doc.doctype = doctype; + if(doctype){ + doc.appendChild(doctype); + } + if(qualifiedName){ + var root = doc.createElementNS(namespaceURI,qualifiedName); + doc.appendChild(root); + } + return doc; + }, + // Introduced in DOM Level 2: + createDocumentType:function(qualifiedName, publicId, systemId){// raises:INVALID_CHARACTER_ERR,NAMESPACE_ERR + var node = new DocumentType(); + node.name = qualifiedName; + node.nodeName = qualifiedName; + node.publicId = publicId; + node.systemId = systemId; + // Introduced in DOM Level 2: + //readonly attribute DOMString internalSubset; + + //TODO:.. + // readonly attribute NamedNodeMap entities; + // readonly attribute NamedNodeMap notations; + return node; + } +}; + + +/** + * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-1950641247 + */ + +function Node() { +}; + +Node.prototype = { + firstChild : null, + lastChild : null, + previousSibling : null, + nextSibling : null, + attributes : null, + parentNode : null, + childNodes : null, + ownerDocument : null, + nodeValue : null, + namespaceURI : null, + prefix : null, + localName : null, + // Modified in DOM Level 2: + insertBefore:function(newChild, refChild){//raises + return _insertBefore(this,newChild,refChild); + }, + replaceChild:function(newChild, oldChild){//raises + this.insertBefore(newChild,oldChild); + if(oldChild){ + this.removeChild(oldChild); + } + }, + removeChild:function(oldChild){ + return _removeChild(this,oldChild); + }, + appendChild:function(newChild){ + return this.insertBefore(newChild,null); + }, + hasChildNodes:function(){ + return this.firstChild != null; + }, + cloneNode:function(deep){ + return cloneNode(this.ownerDocument||this,this,deep); + }, + // Modified in DOM Level 2: + normalize:function(){ + var child = this.firstChild; + while(child){ + var next = child.nextSibling; + if(next && next.nodeType == TEXT_NODE && child.nodeType == TEXT_NODE){ + this.removeChild(next); + child.appendData(next.data); + }else{ + child.normalize(); + child = next; + } + } + }, + // Introduced in DOM Level 2: + isSupported:function(feature, version){ + return this.ownerDocument.implementation.hasFeature(feature,version); + }, + // Introduced in DOM Level 2: + hasAttributes:function(){ + return this.attributes.length>0; + }, + lookupPrefix:function(namespaceURI){ + var el = this; + while(el){ + var map = el._nsMap; + //console.dir(map) + if(map){ + for(var n in map){ + if(map[n] == namespaceURI){ + return n; + } + } + } + el = el.nodeType == ATTRIBUTE_NODE?el.ownerDocument : el.parentNode; + } + return null; + }, + // Introduced in DOM Level 3: + lookupNamespaceURI:function(prefix){ + var el = this; + while(el){ + var map = el._nsMap; + //console.dir(map) + if(map){ + if(prefix in map){ + return map[prefix] ; + } + } + el = el.nodeType == ATTRIBUTE_NODE?el.ownerDocument : el.parentNode; + } + return null; + }, + // Introduced in DOM Level 3: + isDefaultNamespace:function(namespaceURI){ + var prefix = this.lookupPrefix(namespaceURI); + return prefix == null; + } +}; + + +function _xmlEncoder(c){ + return c == '<' && '<' || + c == '>' && '>' || + c == '&' && '&' || + c == '"' && '"' || + '&#'+c.charCodeAt()+';' +} + + +copy(NodeType,Node); +copy(NodeType,Node.prototype); + +/** + * @param callback return true for continue,false for break + * @return boolean true: break visit; + */ +function _visitNode(node,callback){ + if(callback(node)){ + return true; + } + if(node = node.firstChild){ + do{ + if(_visitNode(node,callback)){return true} + }while(node=node.nextSibling) + } +} + + + +function Document(){ +} +function _onAddAttribute(doc,el,newAttr){ + doc && doc._inc++; + var ns = newAttr.namespaceURI ; + if(ns == 'http://www.w3.org/2000/xmlns/'){ + //update namespace + el._nsMap[newAttr.prefix?newAttr.localName:''] = newAttr.value + } +} +function _onRemoveAttribute(doc,el,newAttr,remove){ + doc && doc._inc++; + var ns = newAttr.namespaceURI ; + if(ns == 'http://www.w3.org/2000/xmlns/'){ + //update namespace + delete el._nsMap[newAttr.prefix?newAttr.localName:''] + } +} +function _onUpdateChild(doc,el,newChild){ + if(doc && doc._inc){ + doc._inc++; + //update childNodes + var cs = el.childNodes; + if(newChild){ + cs[cs.length++] = newChild; + }else{ + //console.log(1) + var child = el.firstChild; + var i = 0; + while(child){ + cs[i++] = child; + child =child.nextSibling; + } + cs.length = i; + } + } +} + +/** + * attributes; + * children; + * + * writeable properties: + * nodeValue,Attr:value,CharacterData:data + * prefix + */ +function _removeChild(parentNode,child){ + var previous = child.previousSibling; + var next = child.nextSibling; + if(previous){ + previous.nextSibling = next; + }else{ + parentNode.firstChild = next + } + if(next){ + next.previousSibling = previous; + }else{ + parentNode.lastChild = previous; + } + _onUpdateChild(parentNode.ownerDocument,parentNode); + return child; +} +/** + * preformance key(refChild == null) + */ +function _insertBefore(parentNode,newChild,nextChild){ + var cp = newChild.parentNode; + if(cp){ + cp.removeChild(newChild);//remove and update + } + if(newChild.nodeType === DOCUMENT_FRAGMENT_NODE){ + var newFirst = newChild.firstChild; + if (newFirst == null) { + return newChild; + } + var newLast = newChild.lastChild; + }else{ + newFirst = newLast = newChild; + } + var pre = nextChild ? nextChild.previousSibling : parentNode.lastChild; + + newFirst.previousSibling = pre; + newLast.nextSibling = nextChild; + + + if(pre){ + pre.nextSibling = newFirst; + }else{ + parentNode.firstChild = newFirst; + } + if(nextChild == null){ + parentNode.lastChild = newLast; + }else{ + nextChild.previousSibling = newLast; + } + do{ + newFirst.parentNode = parentNode; + }while(newFirst !== newLast && (newFirst= newFirst.nextSibling)) + _onUpdateChild(parentNode.ownerDocument||parentNode,parentNode); + //console.log(parentNode.lastChild.nextSibling == null) + if (newChild.nodeType == DOCUMENT_FRAGMENT_NODE) { + newChild.firstChild = newChild.lastChild = null; + } + return newChild; +} +function _appendSingleChild(parentNode,newChild){ + var cp = newChild.parentNode; + if(cp){ + var pre = parentNode.lastChild; + cp.removeChild(newChild);//remove and update + var pre = parentNode.lastChild; + } + var pre = parentNode.lastChild; + newChild.parentNode = parentNode; + newChild.previousSibling = pre; + newChild.nextSibling = null; + if(pre){ + pre.nextSibling = newChild; + }else{ + parentNode.firstChild = newChild; + } + parentNode.lastChild = newChild; + _onUpdateChild(parentNode.ownerDocument,parentNode,newChild); + return newChild; + //console.log("__aa",parentNode.lastChild.nextSibling == null) +} +Document.prototype = { + //implementation : null, + nodeName : '#document', + nodeType : DOCUMENT_NODE, + doctype : null, + documentElement : null, + _inc : 1, + + insertBefore : function(newChild, refChild){//raises + if(newChild.nodeType == DOCUMENT_FRAGMENT_NODE){ + var child = newChild.firstChild; + while(child){ + var next = child.nextSibling; + this.insertBefore(child,refChild); + child = next; + } + return newChild; + } + if(this.documentElement == null && newChild.nodeType == ELEMENT_NODE){ + this.documentElement = newChild; + } + + return _insertBefore(this,newChild,refChild),(newChild.ownerDocument = this),newChild; + }, + removeChild : function(oldChild){ + if(this.documentElement == oldChild){ + this.documentElement = null; + } + return _removeChild(this,oldChild); + }, + // Introduced in DOM Level 2: + importNode : function(importedNode,deep){ + return importNode(this,importedNode,deep); + }, + // Introduced in DOM Level 2: + getElementById : function(id){ + var rtv = null; + _visitNode(this.documentElement,function(node){ + if(node.nodeType == ELEMENT_NODE){ + if(node.getAttribute('id') == id){ + rtv = node; + return true; + } + } + }) + return rtv; + }, + + //document factory method: + createElement : function(tagName){ + var node = new Element(); + node.ownerDocument = this; + node.nodeName = tagName; + node.tagName = tagName; + node.childNodes = new NodeList(); + var attrs = node.attributes = new NamedNodeMap(); + attrs._ownerElement = node; + return node; + }, + createDocumentFragment : function(){ + var node = new DocumentFragment(); + node.ownerDocument = this; + node.childNodes = new NodeList(); + return node; + }, + createTextNode : function(data){ + var node = new Text(); + node.ownerDocument = this; + node.appendData(data) + return node; + }, + createComment : function(data){ + var node = new Comment(); + node.ownerDocument = this; + node.appendData(data) + return node; + }, + createCDATASection : function(data){ + var node = new CDATASection(); + node.ownerDocument = this; + node.appendData(data) + return node; + }, + createProcessingInstruction : function(target,data){ + var node = new ProcessingInstruction(); + node.ownerDocument = this; + node.tagName = node.target = target; + node.nodeValue= node.data = data; + return node; + }, + createAttribute : function(name){ + var node = new Attr(); + node.ownerDocument = this; + node.name = name; + node.nodeName = name; + node.localName = name; + node.specified = true; + return node; + }, + createEntityReference : function(name){ + var node = new EntityReference(); + node.ownerDocument = this; + node.nodeName = name; + return node; + }, + // Introduced in DOM Level 2: + createElementNS : function(namespaceURI,qualifiedName){ + var node = new Element(); + var pl = qualifiedName.split(':'); + var attrs = node.attributes = new NamedNodeMap(); + node.childNodes = new NodeList(); + node.ownerDocument = this; + node.nodeName = qualifiedName; + node.tagName = qualifiedName; + node.namespaceURI = namespaceURI; + if(pl.length == 2){ + node.prefix = pl[0]; + node.localName = pl[1]; + }else{ + //el.prefix = null; + node.localName = qualifiedName; + } + attrs._ownerElement = node; + return node; + }, + // Introduced in DOM Level 2: + createAttributeNS : function(namespaceURI,qualifiedName){ + var node = new Attr(); + var pl = qualifiedName.split(':'); + node.ownerDocument = this; + node.nodeName = qualifiedName; + node.name = qualifiedName; + node.namespaceURI = namespaceURI; + node.specified = true; + if(pl.length == 2){ + node.prefix = pl[0]; + node.localName = pl[1]; + }else{ + //el.prefix = null; + node.localName = qualifiedName; + } + return node; + } +}; +_extends(Document,Node); + + +function Element() { + this._nsMap = {}; +}; +Element.prototype = { + nodeType : ELEMENT_NODE, + hasAttribute : function(name){ + return this.getAttributeNode(name)!=null; + }, + getAttribute : function(name){ + var attr = this.getAttributeNode(name); + return attr && attr.value || ''; + }, + getAttributeNode : function(name){ + return this.attributes.getNamedItem(name); + }, + setAttribute : function(name, value){ + var attr = this.ownerDocument.createAttribute(name); + attr.value = attr.nodeValue = "" + value; + this.setAttributeNode(attr) + }, + removeAttribute : function(name){ + var attr = this.getAttributeNode(name) + attr && this.removeAttributeNode(attr); + }, + + //four real opeartion method + appendChild:function(newChild){ + if(newChild.nodeType === DOCUMENT_FRAGMENT_NODE){ + return this.insertBefore(newChild,null); + }else{ + return _appendSingleChild(this,newChild); + } + }, + setAttributeNode : function(newAttr){ + return this.attributes.setNamedItem(newAttr); + }, + setAttributeNodeNS : function(newAttr){ + return this.attributes.setNamedItemNS(newAttr); + }, + removeAttributeNode : function(oldAttr){ + //console.log(this == oldAttr.ownerElement) + return this.attributes.removeNamedItem(oldAttr.nodeName); + }, + //get real attribute name,and remove it by removeAttributeNode + removeAttributeNS : function(namespaceURI, localName){ + var old = this.getAttributeNodeNS(namespaceURI, localName); + old && this.removeAttributeNode(old); + }, + + hasAttributeNS : function(namespaceURI, localName){ + return this.getAttributeNodeNS(namespaceURI, localName)!=null; + }, + getAttributeNS : function(namespaceURI, localName){ + var attr = this.getAttributeNodeNS(namespaceURI, localName); + return attr && attr.value || ''; + }, + setAttributeNS : function(namespaceURI, qualifiedName, value){ + var attr = this.ownerDocument.createAttributeNS(namespaceURI, qualifiedName); + attr.value = attr.nodeValue = "" + value; + this.setAttributeNode(attr) + }, + getAttributeNodeNS : function(namespaceURI, localName){ + return this.attributes.getNamedItemNS(namespaceURI, localName); + }, + + getElementsByTagName : function(tagName){ + return new LiveNodeList(this,function(base){ + var ls = []; + _visitNode(base,function(node){ + if(node !== base && node.nodeType == ELEMENT_NODE && (tagName === '*' || node.tagName == tagName)){ + ls.push(node); + } + }); + return ls; + }); + }, + getElementsByTagNameNS : function(namespaceURI, localName){ + return new LiveNodeList(this,function(base){ + var ls = []; + _visitNode(base,function(node){ + if(node !== base && node.nodeType === ELEMENT_NODE && (namespaceURI === '*' || node.namespaceURI === namespaceURI) && (localName === '*' || node.localName == localName)){ + ls.push(node); + } + }); + return ls; + + }); + } +}; +Document.prototype.getElementsByTagName = Element.prototype.getElementsByTagName; +Document.prototype.getElementsByTagNameNS = Element.prototype.getElementsByTagNameNS; + + +_extends(Element,Node); +function Attr() { +}; +Attr.prototype.nodeType = ATTRIBUTE_NODE; +_extends(Attr,Node); + + +function CharacterData() { +}; +CharacterData.prototype = { + data : '', + substringData : function(offset, count) { + return this.data.substring(offset, offset+count); + }, + appendData: function(text) { + text = this.data+text; + this.nodeValue = this.data = text; + this.length = text.length; + }, + insertData: function(offset,text) { + this.replaceData(offset,0,text); + + }, + appendChild:function(newChild){ + throw new Error(ExceptionMessage[HIERARCHY_REQUEST_ERR]) + }, + deleteData: function(offset, count) { + this.replaceData(offset,count,""); + }, + replaceData: function(offset, count, text) { + var start = this.data.substring(0,offset); + var end = this.data.substring(offset+count); + text = start + text + end; + this.nodeValue = this.data = text; + this.length = text.length; + } +} +_extends(CharacterData,Node); +function Text() { +}; +Text.prototype = { + nodeName : "#text", + nodeType : TEXT_NODE, + splitText : function(offset) { + var text = this.data; + var newText = text.substring(offset); + text = text.substring(0, offset); + this.data = this.nodeValue = text; + this.length = text.length; + var newNode = this.ownerDocument.createTextNode(newText); + if(this.parentNode){ + this.parentNode.insertBefore(newNode, this.nextSibling); + } + return newNode; + } +} +_extends(Text,CharacterData); +function Comment() { +}; +Comment.prototype = { + nodeName : "#comment", + nodeType : COMMENT_NODE +} +_extends(Comment,CharacterData); + +function CDATASection() { +}; +CDATASection.prototype = { + nodeName : "#cdata-section", + nodeType : CDATA_SECTION_NODE +} +_extends(CDATASection,CharacterData); + + +function DocumentType() { +}; +DocumentType.prototype.nodeType = DOCUMENT_TYPE_NODE; +_extends(DocumentType,Node); + +function Notation() { +}; +Notation.prototype.nodeType = NOTATION_NODE; +_extends(Notation,Node); + +function Entity() { +}; +Entity.prototype.nodeType = ENTITY_NODE; +_extends(Entity,Node); + +function EntityReference() { +}; +EntityReference.prototype.nodeType = ENTITY_REFERENCE_NODE; +_extends(EntityReference,Node); + +function DocumentFragment() { +}; +DocumentFragment.prototype.nodeName = "#document-fragment"; +DocumentFragment.prototype.nodeType = DOCUMENT_FRAGMENT_NODE; +_extends(DocumentFragment,Node); + + +function ProcessingInstruction() { +} +ProcessingInstruction.prototype.nodeType = PROCESSING_INSTRUCTION_NODE; +_extends(ProcessingInstruction,Node); +function XMLSerializer(){} +XMLSerializer.prototype.serializeToString = function(node,isHtml,nodeFilter){ + return nodeSerializeToString.call(node,isHtml,nodeFilter); +} +Node.prototype.toString = nodeSerializeToString; +function nodeSerializeToString(isHtml,nodeFilter){ + var buf = []; + var refNode = this.nodeType == 9?this.documentElement:this; + var prefix = refNode.prefix; + var uri = refNode.namespaceURI; + + if(uri && prefix == null){ + //console.log(prefix) + var prefix = refNode.lookupPrefix(uri); + if(prefix == null){ + //isHTML = true; + var visibleNamespaces=[ + {namespace:uri,prefix:null} + //{namespace:uri,prefix:''} + ] + } + } + serializeToString(this,buf,isHtml,nodeFilter,visibleNamespaces); + //console.log('###',this.nodeType,uri,prefix,buf.join('')) + return buf.join(''); +} +function needNamespaceDefine(node,isHTML, visibleNamespaces) { + var prefix = node.prefix||''; + var uri = node.namespaceURI; + if (!prefix && !uri){ + return false; + } + if (prefix === "xml" && uri === "http://www.w3.org/XML/1998/namespace" + || uri == 'http://www.w3.org/2000/xmlns/'){ + return false; + } + + var i = visibleNamespaces.length + //console.log('@@@@',node.tagName,prefix,uri,visibleNamespaces) + while (i--) { + var ns = visibleNamespaces[i]; + // get namespace prefix + //console.log(node.nodeType,node.tagName,ns.prefix,prefix) + if (ns.prefix == prefix){ + return ns.namespace != uri; + } + } + //console.log(isHTML,uri,prefix=='') + //if(isHTML && prefix ==null && uri == 'http://www.w3.org/1999/xhtml'){ + // return false; + //} + //node.flag = '11111' + //console.error(3,true,node.flag,node.prefix,node.namespaceURI) + return true; +} +function serializeToString(node,buf,isHTML,nodeFilter,visibleNamespaces){ + if(nodeFilter){ + node = nodeFilter(node); + if(node){ + if(typeof node == 'string'){ + buf.push(node); + return; + } + }else{ + return; + } + //buf.sort.apply(attrs, attributeSorter); + } + switch(node.nodeType){ + case ELEMENT_NODE: + if (!visibleNamespaces) visibleNamespaces = []; + var startVisibleNamespaces = visibleNamespaces.length; + var attrs = node.attributes; + var len = attrs.length; + var child = node.firstChild; + var nodeName = node.tagName; + + isHTML = (htmlns === node.namespaceURI) ||isHTML + buf.push('<',nodeName); + + + + for(var i=0;i'); + //if is cdata child node + if(isHTML && /^script$/i.test(nodeName)){ + while(child){ + if(child.data){ + buf.push(child.data); + }else{ + serializeToString(child,buf,isHTML,nodeFilter,visibleNamespaces); + } + child = child.nextSibling; + } + }else + { + while(child){ + serializeToString(child,buf,isHTML,nodeFilter,visibleNamespaces); + child = child.nextSibling; + } + } + buf.push(''); + }else{ + buf.push('/>'); + } + // remove added visible namespaces + //visibleNamespaces.length = startVisibleNamespaces; + return; + case DOCUMENT_NODE: + case DOCUMENT_FRAGMENT_NODE: + var child = node.firstChild; + while(child){ + serializeToString(child,buf,isHTML,nodeFilter,visibleNamespaces); + child = child.nextSibling; + } + return; + case ATTRIBUTE_NODE: + return buf.push(' ',node.name,'="',node.value.replace(/[<&"]/g,_xmlEncoder),'"'); + case TEXT_NODE: + return buf.push(node.data.replace(/[<&]/g,_xmlEncoder)); + case CDATA_SECTION_NODE: + return buf.push( ''); + case COMMENT_NODE: + return buf.push( ""); + case DOCUMENT_TYPE_NODE: + var pubid = node.publicId; + var sysid = node.systemId; + buf.push(''); + }else if(sysid && sysid!='.'){ + buf.push(' SYSTEM "',sysid,'">'); + }else{ + var sub = node.internalSubset; + if(sub){ + buf.push(" [",sub,"]"); + } + buf.push(">"); + } + return; + case PROCESSING_INSTRUCTION_NODE: + return buf.push( ""); + case ENTITY_REFERENCE_NODE: + return buf.push( '&',node.nodeName,';'); + //case ENTITY_NODE: + //case NOTATION_NODE: + default: + buf.push('??',node.nodeName); + } +} +function importNode(doc,node,deep){ + var node2; + switch (node.nodeType) { + case ELEMENT_NODE: + node2 = node.cloneNode(false); + node2.ownerDocument = doc; + //var attrs = node2.attributes; + //var len = attrs.length; + //for(var i=0;i','amp':'&','quot':'"','apos':"'"} + if(locator){ + domBuilder.setDocumentLocator(locator) + } + + sax.errorHandler = buildErrorHandler(errorHandler,domBuilder,locator); + sax.domBuilder = options.domBuilder || domBuilder; + if(/\/x?html?$/.test(mimeType)){ + entityMap.nbsp = '\xa0'; + entityMap.copy = '\xa9'; + defaultNSMap['']= 'http://www.w3.org/1999/xhtml'; + } + defaultNSMap.xml = defaultNSMap.xml || 'http://www.w3.org/XML/1998/namespace'; + if(source){ + sax.parse(source,defaultNSMap,entityMap); + }else{ + sax.errorHandler.error("invalid doc source"); + } + return domBuilder.doc; +} +function buildErrorHandler(errorImpl,domBuilder,locator){ + if(!errorImpl){ + if(domBuilder instanceof DOMHandler){ + return domBuilder; + } + errorImpl = domBuilder ; + } + var errorHandler = {} + var isCallback = errorImpl instanceof Function; + locator = locator||{} + function build(key){ + var fn = errorImpl[key]; + if(!fn && isCallback){ + fn = errorImpl.length == 2?function(msg){errorImpl(key,msg)}:errorImpl; + } + errorHandler[key] = fn && function(msg){ + fn('[xmldom '+key+']\t'+msg+_locator(locator)); + }||function(){}; + } + build('warning'); + build('error'); + build('fatalError'); + return errorHandler; +} + +//console.log('#\n\n\n\n\n\n\n####') +/** + * +ContentHandler+ErrorHandler + * +LexicalHandler+EntityResolver2 + * -DeclHandler-DTDHandler + * + * DefaultHandler:EntityResolver, DTDHandler, ContentHandler, ErrorHandler + * DefaultHandler2:DefaultHandler,LexicalHandler, DeclHandler, EntityResolver2 + * @link http://www.saxproject.org/apidoc/org/xml/sax/helpers/DefaultHandler.html + */ +function DOMHandler() { + this.cdata = false; +} +function position(locator,node){ + node.lineNumber = locator.lineNumber; + node.columnNumber = locator.columnNumber; +} +/** + * @see org.xml.sax.ContentHandler#startDocument + * @link http://www.saxproject.org/apidoc/org/xml/sax/ContentHandler.html + */ +DOMHandler.prototype = { + startDocument : function() { + this.doc = new DOMImplementation().createDocument(null, null, null); + if (this.locator) { + this.doc.documentURI = this.locator.systemId; + } + }, + startElement:function(namespaceURI, localName, qName, attrs) { + var doc = this.doc; + var el = doc.createElementNS(namespaceURI, qName||localName); + var len = attrs.length; + appendElement(this, el); + this.currentElement = el; + + this.locator && position(this.locator,el) + for (var i = 0 ; i < len; i++) { + var namespaceURI = attrs.getURI(i); + var value = attrs.getValue(i); + var qName = attrs.getQName(i); + var attr = doc.createAttributeNS(namespaceURI, qName); + this.locator &&position(attrs.getLocator(i),attr); + attr.value = attr.nodeValue = value; + el.setAttributeNode(attr) + } + }, + endElement:function(namespaceURI, localName, qName) { + var current = this.currentElement + var tagName = current.tagName; + this.currentElement = current.parentNode; + }, + startPrefixMapping:function(prefix, uri) { + }, + endPrefixMapping:function(prefix) { + }, + processingInstruction:function(target, data) { + var ins = this.doc.createProcessingInstruction(target, data); + this.locator && position(this.locator,ins) + appendElement(this, ins); + }, + ignorableWhitespace:function(ch, start, length) { + }, + characters:function(chars, start, length) { + chars = _toString.apply(this,arguments) + //console.log(chars) + if(chars){ + if (this.cdata) { + var charNode = this.doc.createCDATASection(chars); + } else { + var charNode = this.doc.createTextNode(chars); + } + if(this.currentElement){ + this.currentElement.appendChild(charNode); + }else if(/^\s*$/.test(chars)){ + this.doc.appendChild(charNode); + //process xml + } + this.locator && position(this.locator,charNode) + } + }, + skippedEntity:function(name) { + }, + endDocument:function() { + this.doc.normalize(); + }, + setDocumentLocator:function (locator) { + if(this.locator = locator){// && !('lineNumber' in locator)){ + locator.lineNumber = 0; + } + }, + //LexicalHandler + comment:function(chars, start, length) { + chars = _toString.apply(this,arguments) + var comm = this.doc.createComment(chars); + this.locator && position(this.locator,comm) + appendElement(this, comm); + }, + + startCDATA:function() { + //used in characters() methods + this.cdata = true; + }, + endCDATA:function() { + this.cdata = false; + }, + + startDTD:function(name, publicId, systemId) { + var impl = this.doc.implementation; + if (impl && impl.createDocumentType) { + var dt = impl.createDocumentType(name, publicId, systemId); + this.locator && position(this.locator,dt) + appendElement(this, dt); + } + }, + /** + * @see org.xml.sax.ErrorHandler + * @link http://www.saxproject.org/apidoc/org/xml/sax/ErrorHandler.html + */ + warning:function(error) { + console.warn('[xmldom warning]\t'+error,_locator(this.locator)); + }, + error:function(error) { + console.error('[xmldom error]\t'+error,_locator(this.locator)); + }, + fatalError:function(error) { + console.error('[xmldom fatalError]\t'+error,_locator(this.locator)); + throw error; + } +} +function _locator(l){ + if(l){ + return '\n@'+(l.systemId ||'')+'#[line:'+l.lineNumber+',col:'+l.columnNumber+']' + } +} +function _toString(chars,start,length){ + if(typeof chars == 'string'){ + return chars.substr(start,length) + }else{//java sax connect width xmldom on rhino(what about: "? && !(chars instanceof String)") + if(chars.length >= start+length || start){ + return new java.lang.String(chars,start,length)+''; + } + return chars; + } +} + +/* + * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/LexicalHandler.html + * used method of org.xml.sax.ext.LexicalHandler: + * #comment(chars, start, length) + * #startCDATA() + * #endCDATA() + * #startDTD(name, publicId, systemId) + * + * + * IGNORED method of org.xml.sax.ext.LexicalHandler: + * #endDTD() + * #startEntity(name) + * #endEntity(name) + * + * + * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/DeclHandler.html + * IGNORED method of org.xml.sax.ext.DeclHandler + * #attributeDecl(eName, aName, type, mode, value) + * #elementDecl(name, model) + * #externalEntityDecl(name, publicId, systemId) + * #internalEntityDecl(name, value) + * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/EntityResolver2.html + * IGNORED method of org.xml.sax.EntityResolver2 + * #resolveEntity(String name,String publicId,String baseURI,String systemId) + * #resolveEntity(publicId, systemId) + * #getExternalSubset(name, baseURI) + * @link http://www.saxproject.org/apidoc/org/xml/sax/DTDHandler.html + * IGNORED method of org.xml.sax.DTDHandler + * #notationDecl(name, publicId, systemId) {}; + * #unparsedEntityDecl(name, publicId, systemId, notationName) {}; + */ +"endDTD,startEntity,endEntity,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,resolveEntity,getExternalSubset,notationDecl,unparsedEntityDecl".replace(/\w+/g,function(key){ + DOMHandler.prototype[key] = function(){return null} +}) + +/* Private static helpers treated below as private instance methods, so don't need to add these to the public API; we might use a Relator to also get rid of non-standard public properties */ +function appendElement (hander,node) { + if (!hander.currentElement) { + hander.doc.appendChild(node); + } else { + hander.currentElement.appendChild(node); + } +}//appendChild and setAttributeNS are preformance key + +//if(typeof require == 'function'){ + var XMLReader = __webpack_require__(3).XMLReader; + var DOMImplementation = exports.DOMImplementation = __webpack_require__(0).DOMImplementation; + exports.XMLSerializer = __webpack_require__(0).XMLSerializer ; + exports.DOMParser = DOMParser; +//} + + +/***/ }, +/* 3 */ +/***/ function(module, exports) { + +//[4] NameStartChar ::= ":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | [#x370-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF] +//[4a] NameChar ::= NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040] +//[5] Name ::= NameStartChar (NameChar)* +var nameStartChar = /[A-Z_a-z\xC0-\xD6\xD8-\xF6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]///\u10000-\uEFFFF +var nameChar = new RegExp("[\\-\\.0-9"+nameStartChar.source.slice(1,-1)+"\\u00B7\\u0300-\\u036F\\u203F-\\u2040]"); +var tagNamePattern = new RegExp('^'+nameStartChar.source+nameChar.source+'*(?:\:'+nameStartChar.source+nameChar.source+'*)?$'); +//var tagNamePattern = /^[a-zA-Z_][\w\-\.]*(?:\:[a-zA-Z_][\w\-\.]*)?$/ +//var handlers = 'resolveEntity,getExternalSubset,characters,endDocument,endElement,endPrefixMapping,ignorableWhitespace,processingInstruction,setDocumentLocator,skippedEntity,startDocument,startElement,startPrefixMapping,notationDecl,unparsedEntityDecl,error,fatalError,warning,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,comment,endCDATA,endDTD,endEntity,startCDATA,startDTD,startEntity'.split(',') + +//S_TAG, S_ATTR, S_EQ, S_ATTR_NOQUOT_VALUE +//S_ATTR_SPACE, S_ATTR_END, S_TAG_SPACE, S_TAG_CLOSE +var S_TAG = 0;//tag name offerring +var S_ATTR = 1;//attr name offerring +var S_ATTR_SPACE=2;//attr name end and space offer +var S_EQ = 3;//=space? +var S_ATTR_NOQUOT_VALUE = 4;//attr value(no quot value only) +var S_ATTR_END = 5;//attr value end and no space(quot end) +var S_TAG_SPACE = 6;//(attr value end || tag end ) && (space offer) +var S_TAG_CLOSE = 7;//closed el + +function XMLReader(){ + +} + +XMLReader.prototype = { + parse:function(source,defaultNSMap,entityMap){ + var domBuilder = this.domBuilder; + domBuilder.startDocument(); + _copy(defaultNSMap ,defaultNSMap = {}) + parse(source,defaultNSMap,entityMap, + domBuilder,this.errorHandler); + domBuilder.endDocument(); + } +} +function parse(source,defaultNSMapCopy,entityMap,domBuilder,errorHandler){ + function fixedFromCharCode(code) { + // String.prototype.fromCharCode does not supports + // > 2 bytes unicode chars directly + if (code > 0xffff) { + code -= 0x10000; + var surrogate1 = 0xd800 + (code >> 10) + , surrogate2 = 0xdc00 + (code & 0x3ff); + + return String.fromCharCode(surrogate1, surrogate2); + } else { + return String.fromCharCode(code); + } + } + function entityReplacer(a){ + var k = a.slice(1,-1); + if(k in entityMap){ + return entityMap[k]; + }else if(k.charAt(0) === '#'){ + return fixedFromCharCode(parseInt(k.substr(1).replace('x','0x'))) + }else{ + errorHandler.error('entity not found:'+a); + return a; + } + } + function appendText(end){//has some bugs + if(end>start){ + var xt = source.substring(start,end).replace(/&#?\w+;/g,entityReplacer); + locator&&position(start); + domBuilder.characters(xt,0,end-start); + start = end + } + } + function position(p,m){ + while(p>=lineEnd && (m = linePattern.exec(source))){ + lineStart = m.index; + lineEnd = lineStart + m[0].length; + locator.lineNumber++; + //console.log('line++:',locator,startPos,endPos) + } + locator.columnNumber = p-lineStart+1; + } + var lineStart = 0; + var lineEnd = 0; + var linePattern = /.*(?:\r\n?|\n)|.*$/g + var locator = domBuilder.locator; + + var parseStack = [{currentNSMap:defaultNSMapCopy}] + var closeMap = {}; + var start = 0; + while(true){ + try{ + var tagStart = source.indexOf('<',start); + if(tagStart<0){ + if(!source.substr(start).match(/^\s*$/)){ + var doc = domBuilder.doc; + var text = doc.createTextNode(source.substr(start)); + doc.appendChild(text); + domBuilder.currentElement = text; + } + return; + } + if(tagStart>start){ + appendText(tagStart); + } + switch(source.charAt(tagStart+1)){ + case '/': + var end = source.indexOf('>',tagStart+3); + var tagName = source.substring(tagStart+2,end); + var config = parseStack.pop(); + if(end<0){ + + tagName = source.substring(tagStart+2).replace(/[\s<].*/,''); + //console.error('#@@@@@@'+tagName) + errorHandler.error("end tag name: "+tagName+' is not complete:'+config.tagName); + end = tagStart+1+tagName.length; + }else if(tagName.match(/\s + locator&&position(tagStart); + end = parseInstruction(source,tagStart,domBuilder); + break; + case '!':// start){ + start = end; + }else{ + //TODO: 这里有可能sax回退,有位置错误风险 + appendText(Math.max(tagStart,start)+1); + } + } +} +function copyLocator(f,t){ + t.lineNumber = f.lineNumber; + t.columnNumber = f.columnNumber; + return t; +} + +/** + * @see #appendElement(source,elStartEnd,el,selfClosed,entityReplacer,domBuilder,parseStack); + * @return end of the elementStartPart(end of elementEndPart for selfClosed el) + */ +function parseElementStartPart(source,start,el,currentNSMap,entityReplacer,errorHandler){ + var attrName; + var value; + var p = ++start; + var s = S_TAG;//status + while(true){ + var c = source.charAt(p); + switch(c){ + case '=': + if(s === S_ATTR){//attrName + attrName = source.slice(start,p); + s = S_EQ; + }else if(s === S_ATTR_SPACE){ + s = S_EQ; + }else{ + //fatalError: equal must after attrName or space after attrName + throw new Error('attribute equal must after attrName'); + } + break; + case '\'': + case '"': + if(s === S_EQ || s === S_ATTR //|| s == S_ATTR_SPACE + ){//equal + if(s === S_ATTR){ + errorHandler.warning('attribute value must after "="') + attrName = source.slice(start,p) + } + start = p+1; + p = source.indexOf(c,start) + if(p>0){ + value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer); + el.add(attrName,value,start-1); + s = S_ATTR_END; + }else{ + //fatalError: no end quot match + throw new Error('attribute value no end \''+c+'\' match'); + } + }else if(s == S_ATTR_NOQUOT_VALUE){ + value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer); + //console.log(attrName,value,start,p) + el.add(attrName,value,start); + //console.dir(el) + errorHandler.warning('attribute "'+attrName+'" missed start quot('+c+')!!'); + start = p+1; + s = S_ATTR_END + }else{ + //fatalError: no equal before + throw new Error('attribute value must after "="'); + } + break; + case '/': + switch(s){ + case S_TAG: + el.setTagName(source.slice(start,p)); + case S_ATTR_END: + case S_TAG_SPACE: + case S_TAG_CLOSE: + s =S_TAG_CLOSE; + el.closed = true; + case S_ATTR_NOQUOT_VALUE: + case S_ATTR: + case S_ATTR_SPACE: + break; + //case S_EQ: + default: + throw new Error("attribute invalid close char('/')") + } + break; + case ''://end document + //throw new Error('unexpected end of input') + errorHandler.error('unexpected end of input'); + if(s == S_TAG){ + el.setTagName(source.slice(start,p)); + } + return p; + case '>': + switch(s){ + case S_TAG: + el.setTagName(source.slice(start,p)); + case S_ATTR_END: + case S_TAG_SPACE: + case S_TAG_CLOSE: + break;//normal + case S_ATTR_NOQUOT_VALUE://Compatible state + case S_ATTR: + value = source.slice(start,p); + if(value.slice(-1) === '/'){ + el.closed = true; + value = value.slice(0,-1) + } + case S_ATTR_SPACE: + if(s === S_ATTR_SPACE){ + value = attrName; + } + if(s == S_ATTR_NOQUOT_VALUE){ + errorHandler.warning('attribute "'+value+'" missed quot(")!!'); + el.add(attrName,value.replace(/&#?\w+;/g,entityReplacer),start) + }else{ + if(currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' || !value.match(/^(?:disabled|checked|selected)$/i)){ + errorHandler.warning('attribute "'+value+'" missed value!! "'+value+'" instead!!') + } + el.add(value,value,start) + } + break; + case S_EQ: + throw new Error('attribute value missed!!'); + } +// console.log(tagName,tagNamePattern,tagNamePattern.test(tagName)) + return p; + /*xml space '\x20' | #x9 | #xD | #xA; */ + case '\u0080': + c = ' '; + default: + if(c<= ' '){//space + switch(s){ + case S_TAG: + el.setTagName(source.slice(start,p));//tagName + s = S_TAG_SPACE; + break; + case S_ATTR: + attrName = source.slice(start,p) + s = S_ATTR_SPACE; + break; + case S_ATTR_NOQUOT_VALUE: + var value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer); + errorHandler.warning('attribute "'+value+'" missed quot(")!!'); + el.add(attrName,value,start) + case S_ATTR_END: + s = S_TAG_SPACE; + break; + //case S_TAG_SPACE: + //case S_EQ: + //case S_ATTR_SPACE: + // void();break; + //case S_TAG_CLOSE: + //ignore warning + } + }else{//not space +//S_TAG, S_ATTR, S_EQ, S_ATTR_NOQUOT_VALUE +//S_ATTR_SPACE, S_ATTR_END, S_TAG_SPACE, S_TAG_CLOSE + switch(s){ + //case S_TAG:void();break; + //case S_ATTR:void();break; + //case S_ATTR_NOQUOT_VALUE:void();break; + case S_ATTR_SPACE: + var tagName = el.tagName; + if(currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' || !attrName.match(/^(?:disabled|checked|selected)$/i)){ + errorHandler.warning('attribute "'+attrName+'" missed value!! "'+attrName+'" instead2!!') + } + el.add(attrName,attrName,start); + start = p; + s = S_ATTR; + break; + case S_ATTR_END: + errorHandler.warning('attribute space is required"'+attrName+'"!!') + case S_TAG_SPACE: + s = S_ATTR; + start = p; + break; + case S_EQ: + s = S_ATTR_NOQUOT_VALUE; + start = p; + break; + case S_TAG_CLOSE: + throw new Error("elements closed character '/' and '>' must be connected to"); + } + } + }//end outer switch + //console.log('p++',p) + p++; + } +} +/** + * @return true if has new namespace define + */ +function appendElement(el,domBuilder,currentNSMap){ + var tagName = el.tagName; + var localNSMap = null; + //var currentNSMap = parseStack[parseStack.length-1].currentNSMap; + var i = el.length; + while(i--){ + var a = el[i]; + var qName = a.qName; + var value = a.value; + var nsp = qName.indexOf(':'); + if(nsp>0){ + var prefix = a.prefix = qName.slice(0,nsp); + var localName = qName.slice(nsp+1); + var nsPrefix = prefix === 'xmlns' && localName + }else{ + localName = qName; + prefix = null + nsPrefix = qName === 'xmlns' && '' + } + //can not set prefix,because prefix !== '' + a.localName = localName ; + //prefix == null for no ns prefix attribute + if(nsPrefix !== false){//hack!! + if(localNSMap == null){ + localNSMap = {} + //console.log(currentNSMap,0) + _copy(currentNSMap,currentNSMap={}) + //console.log(currentNSMap,1) + } + currentNSMap[nsPrefix] = localNSMap[nsPrefix] = value; + a.uri = 'http://www.w3.org/2000/xmlns/' + domBuilder.startPrefixMapping(nsPrefix, value) + } + } + var i = el.length; + while(i--){ + a = el[i]; + var prefix = a.prefix; + if(prefix){//no prefix attribute has no namespace + if(prefix === 'xml'){ + a.uri = 'http://www.w3.org/XML/1998/namespace'; + }if(prefix !== 'xmlns'){ + a.uri = currentNSMap[prefix || ''] + + //{console.log('###'+a.qName,domBuilder.locator.systemId+'',currentNSMap,a.uri)} + } + } + } + var nsp = tagName.indexOf(':'); + if(nsp>0){ + prefix = el.prefix = tagName.slice(0,nsp); + localName = el.localName = tagName.slice(nsp+1); + }else{ + prefix = null;//important!! + localName = el.localName = tagName; + } + //no prefix element has default namespace + var ns = el.uri = currentNSMap[prefix || '']; + domBuilder.startElement(ns,localName,tagName,el); + //endPrefixMapping and startPrefixMapping have not any help for dom builder + //localNSMap = null + if(el.closed){ + domBuilder.endElement(ns,localName,tagName); + if(localNSMap){ + for(prefix in localNSMap){ + domBuilder.endPrefixMapping(prefix) + } + } + }else{ + el.currentNSMap = currentNSMap; + el.localNSMap = localNSMap; + //parseStack.push(el); + return true; + } +} +function parseHtmlSpecialContent(source,elStartEnd,tagName,entityReplacer,domBuilder){ + if(/^(?:script|textarea)$/i.test(tagName)){ + var elEndStart = source.indexOf('',elStartEnd); + var text = source.substring(elStartEnd+1,elEndStart); + if(/[&<]/.test(text)){ + if(/^script$/i.test(tagName)){ + //if(!/\]\]>/.test(text)){ + //lexHandler.startCDATA(); + domBuilder.characters(text,0,text.length); + //lexHandler.endCDATA(); + return elEndStart; + //} + }//}else{//text area + text = text.replace(/&#?\w+;/g,entityReplacer); + domBuilder.characters(text,0,text.length); + return elEndStart; + //} + + } + } + return elStartEnd+1; +} +function fixSelfClosed(source,elStartEnd,tagName,closeMap){ + //if(tagName in closeMap){ + var pos = closeMap[tagName]; + if(pos == null){ + //console.log(tagName) + pos = source.lastIndexOf('') + if(pos',start+4); + //append comment source.substring(4,end)// 1.9.0-alpha14 diff --git a/project.clj b/project.clj index 763fdb7..0c3678d 100644 --- a/project.clj +++ b/project.clj @@ -1,6 +1,6 @@ (defproject org.clojure/data.xml "0-UE-DEVELOPMENT" :source-paths ["src/main/clojure"] - :test-paths ["src/test/clojure"] + :test-paths ["src/test/clojure" "src/test/clojurescript"] :resource-paths ["src/test/resources" "target/gen-resources"] :dependencies [[org.clojure/clojure "1.8.0"] [org.clojure/clojurescript "1.9.293"] diff --git a/src/main/clojure/clojure/data/xml/js/dom.cljs b/src/main/clojure/clojure/data/xml/js/dom.cljs index f5b3b55..5851316 100644 --- a/src/main/clojure/clojure/data/xml/js/dom.cljs +++ b/src/main/clojure/clojure/data/xml/js/dom.cljs @@ -91,7 +91,8 @@ (string? el) (text-node el) (instance? Element el) el ;; stupid xmldom, (some? (.-item el)) - (instance? NodeList el) el + #_(instance? NodeList el) + (some? (.-item el)) el (instance? Text el) el (satisfies? ILookup el) (element* (:tag el) (:attrs el) @@ -110,24 +111,27 @@ (identical? xmlns-uri (.-namespaceURI a))) (def remove-xmlns-attrs-xf (remove xmlns-attr?)) (def remove-xmlns-attrs (partial into {} remove-xmlns-attrs-xf)) -(def filter-xmlns-attrs (partial into {} (filter xmlns-attr?))) +(def filter-xmlns-attrs-xf (filter xmlns-attr?)) +(def filter-xmlns-attrs (partial into {} filter-xmlns-attrs-xf)) -(defn dom-element-attrs [el] - (transduce - remove-xmlns-attrs-xf - (completing - (fn [ta attr-node] - (assoc! ta - (dom-element-tag attr-node) - (.-value attr-node))) - persistent!) - (transient {}) - (array-seq el))) +(defn dom-element-attrs + ([el] (dom-element-attrs remove-xmlns-attrs-xf el)) + ([xf el] + (transduce + xf + (completing + (fn [ta attr-node] + (assoc! ta + (dom-element-tag attr-node) + (.-value attr-node))) + persistent!) + (transient {}) + (array-seq el)))) (declare element-data) (defn- node-list-vec [nl] - (mapv element-data (array-seq nl))) + (into [] (map element-data) (array-seq nl))) (defn- as-node [n] (if (instance? Text n) @@ -144,11 +148,9 @@ (node/element* (dom-element-tag el) (dom-element-attrs (.-attributes el)) (node-list-vec (.-childNodes el)) - (do - (prn "META" {:clojure.data.xml/nss (filter-xmlns-attrs - (.-attributes el))}) - {:clojure.data.xml/nss (filter-xmlns-attrs - (.-attributes el))})) + {:clojure.data.xml/nss (dom-element-attrs + filter-xmlns-attrs-xf + (.-attributes el))}) ;;(instance? NamedNodeMap el) (.-getNamedItemNS el) (dom-element-attrs el) @@ -174,11 +176,11 @@ :content (.-childNodes el) (throw "XML tag has no key" {:key k :el el}))) ([el k nf] - (println "Element" k "=>" (case k - :tag (dom-element-tag el) - :attrs (.-attributes el) - :content (.-childNodes el) - nf)) + #_(println "Element" k "=>" (case k + :tag (dom-element-tag el) + :attrs (.-attributes el) + :content (.-childNodes el) + nf)) (case k :tag (dom-element-tag el) :attrs (remove-xmlns-attrs (.-attributes el)) @@ -208,9 +210,9 @@ (.-value i) nil)) ([attrs attr not-found] - (println "Attrs" attr "=>" (if-let [i (.getNamedItemNS attrs (qname-uri attr) (qname-local attr))] - (.-value i) - not-found)) + #_(println "Attrs" attr "=>" (if-let [i (.getNamedItemNS attrs (qname-uri attr) (qname-local attr))] + (.-value i) + not-found)) (if-let [i (.getNamedItemNS attrs (qname-uri attr) (qname-local attr))] (.-value i) not-found))) @@ -229,11 +231,11 @@ init nm)) IEquiv (-equiv [nm0 nm1] - (println "NamedNodeMap.-equiv" (identical? nm0 nm1) (count nm0) (count nm1)) + #_(println "NamedNodeMap.-equiv" (identical? nm0 nm1) (count nm0) (count nm1)) (or (identical? nm0 nm1) (and (identical? (count nm0) (count nm1)) (reduce-kv (fn [_ qn v] - (println "=" v 'qn qn '(get nm1 qn "") (get nm1 qn "")) + #_(println "=" v 'qn qn '(get nm1 qn "") (get nm1 qn "")) (or (identical? v (get nm1 qn "")) (reduced false))) true nm0))))) @@ -254,7 +256,7 @@ nf))) IEquiv (-equiv [nl0 nl1] - (println "NodeList.-equiv") + #_(println "NodeList.-equiv") (or (identical? nl0 nl1) (and (identical? (count nl0) (count nl1)) (reduce (fn [_ n] diff --git a/src/test/clojure/clojure/data/xml/cljs_testsuite.clj b/src/test/clojure/clojure/data/xml/cljs_testsuite.clj new file mode 100644 index 0000000..5e03240 --- /dev/null +++ b/src/test/clojure/clojure/data/xml/cljs_testsuite.clj @@ -0,0 +1,53 @@ +(ns clojure.data.xml.cljs-testsuite + (:require + [clojure.test :refer :all] + [cljs.repl :as repl] + [cljs.repl.nashorn :as repl-nh] + [cljs.closure :as closure] + [cljs.build.api :as bapi] + [clojure.string :as str] + [clojure.java.io :as io]) + (:import + java.nio.file.Files + java.nio.file.attribute.FileAttribute)) + +(defn tempdir [] + (str (Files/createTempDirectory + "cljs-nashorn-" (into-array FileAttribute [])))) + +(defn compile-testsuite! [dir] + (let [out (io/file dir "tests.js")] + (println "Building in" dir) + (bapi/build (bapi/inputs "src/main/clojure" "src/test/clojure" "src/test/clojurescript") + {:output-to (str out) + :output-dir dir + :main 'clojure.data.xml.test-cljs + :optimizations :advanced + :pseudo-names true + :preamble ["dxml-nashorn.generated.js"]}) + (spit (io/file dir "tests.reopt.js") + (closure/optimize {:optimizations :simple + :pretty-print true + :closure-warnings + {:non-standard-jsdoc :off}} + (slurp out))))) + +(defn run-testsuite! [dir] + (System/setProperty "nashorn.persistent.code.cache" "target/nashorn_code_cache") + (let [engine (repl-nh/create-engine)] + (println "INFO" "Running nashorn-repl with" (System/getProperty "nashorn.persistent.code.cache")) + (compile-testsuite! dir) + (.eval engine (io/reader (io/file dir "tests.reopt.js"))) + (let [{:as res :keys [fail error]} (read-string (.eval engine "clojure.data.xml.test_cljs._main_nashorn()"))] + (is (and (zero? fail) (zero? error)) + (pr-str res))))) + +(comment + + (def td (tempdir)) + (def engine (:engine (repl-nh/repl-env))) + (run-testsuite! td) + (.eval engine (io/reader (io/file td "tests.reopt.js"))) + (.eval engine "clojure.data.xml.test_cljs._main()") + + ) diff --git a/src/test/clojure/clojure/data/xml/test_cljs.clj b/src/test/clojure/clojure/data/xml/test_cljs.clj index 330046c..de4c781 100644 --- a/src/test/clojure/clojure/data/xml/test_cljs.clj +++ b/src/test/clojure/clojure/data/xml/test_cljs.clj @@ -9,68 +9,14 @@ (ns ^{:doc "Clojurescript tests for data.xml"} clojure.data.xml.test-cljs (:require - [clojure.test :refer :all] - [cljs.repl :as repl] - [cljs.repl.nashorn :as repl-nh] - [cljs.closure :as closure] - [cljs.build.api :as bapi] - [clojure.string :as str] - [clojure.java.io :as io]) - (:import - java.nio.file.Files - java.nio.file.attribute.FileAttribute)) - -(defn nashorn-env [] - (let [{:as env :keys [engine]} (repl-nh/repl-env)] - (repl-nh/eval-resource engine "dxml-nashorn.generated.js" true) - env)) - -(defn tempdir [] - (str (Files/createTempDirectory - "cljs-nashorn-" (into-array FileAttribute [])))) - -(defn compile-testsuite! [dir] - (let [out (io/file dir "tests.js")] - (println "Building in" dir) - (bapi/build (bapi/inputs "src/main/clojure" "src/test/clojure") - {:output-to (str out) - :output-dir dir - :main 'clojure.data.xml.test-cljs - :optimizations :simple - ;; :pseudo-names true - :preamble ["dxml-nashorn.generated.js"]}) - (spit (io/file dir "tests.reopt.js") - (closure/optimize {:optimizations :simple - :pretty-print true} - (slurp out))))) - -(defn run-testsuite! [dir] - (let [{:keys [engine]} (repl-nh/repl-env)] - (compile-testsuite! dir) - (.eval engine (io/reader (io/file dir "tests.reopt.js"))) - (.eval engine "clojure.data.xml.test_cljs._main()"))) + [clojure.test :refer :all])) (deftest clojurescript-test-suite - (is (= false (run-testsuite! (tempdir))))) - -(comment - - (def td (tempdir)) - (def engine (:engine (repl-nh/repl-env))) - (run-testsuite! td) - (.eval engine (io/reader (io/file td "tests.reopt.js"))) - (.eval engine "clojure.data.xml.test_cljs._main()") - - ) - -#_(deftest clojurescript-test-suite - (is (= :success - (read-string - (repl/evaluate-form - (doto (repl-nh/repl-env :debug true) - (repl/-setup {:output-dir (tempdir)})) - (ana/empty-env) - "TESTSUITE LAUNCHER" - (list 'do - (list 'require ''clojure.data.xml.test-cljs) - (list 'clojure.data.xml.test-cljs/run-tests))))))) + (try + (require 'clojure.data.xml.cljs-testsuite) + (eval '(clojure.data.xml.cljs-testsuite/run-testsuite! "target/cljs-test-nashorn")) + (catch Exception e + (println "WARN: clojurescript test suite not available with Clojure" + *clojure-version* (System/getProperty "java.runtime.name") + (System/getProperty "java.vm.version") (System/getProperty "java.runtime.version") + \newline e)))) diff --git a/src/test/clojure/clojure/data/xml/test_cljs.cljs b/src/test/clojure/clojure/data/xml/test_cljs.cljs deleted file mode 100644 index a593821..0000000 --- a/src/test/clojure/clojure/data/xml/test_cljs.cljs +++ /dev/null @@ -1,16 +0,0 @@ -(ns clojure.data.xml.test-cljs - (:require [cljs.test :as test] - [clojure.data.xml :as xml] - clojure.data.xml.test-cljs-basic - clojure.data.xml.test-cljs-extended)) - -(defn ^:export -main [] - (set! *print-newline* false) - (set! *print-fn* js/print) - (set! *print-err-fn* js/print) - (println "Running Basic Tests") - (test/run-tests 'clojure.data.xml.test-cljs-basic) - (println "Extending DOM Objects and running again + extended tests") - (xml/extend-dom-as-data!) - (test/testing "with extended native dom" - (test/run-tests 'clojure.data.xml.test-cljs-basic 'clojure.data.xml.test-cljs-extended))) diff --git a/src/test/clojure/clojure/data/xml/cljs_repls.clj b/src/test/clojurescript/clojure/data/xml/cljs_repls.clj similarity index 81% rename from src/test/clojure/clojure/data/xml/cljs_repls.clj rename to src/test/clojurescript/clojure/data/xml/cljs_repls.clj index 0539461..4a329ff 100644 --- a/src/test/clojure/clojure/data/xml/cljs_repls.clj +++ b/src/test/clojurescript/clojure/data/xml/cljs_repls.clj @@ -6,6 +6,11 @@ [cljs.closure :as closure] [figwheel-sidecar.repl-api :refer [start-figwheel! stop-figwheel! cljs-repl]])) +(defn nashorn-env [] + (let [{:as env :keys [engine]} (repl-nh/repl-env)] + (repl-nh/eval-resource engine "dxml-nashorn.generated.js" true) + env)) + (def handle-redirect (constantly {:status 307 :headers {"Location" "/cljs-tests/index.html"}})) (defn repl-figwheel! [] @@ -15,7 +20,7 @@ :ring-handler `handle-redirect} :all-builds [{:id "tests" - :source-paths ["src/main/clojure" "src/test/clojure"] + :source-paths ["src/main/clojure" "src/test/clojure" "src/test/clojurescript"] :figwheel {:on-jsload "clojure.data.xml.test-cljs/-main"} :compiler {:main 'clojure.data.xml.test-cljs :preloads '[devtools.preload] diff --git a/src/test/clojurescript/clojure/data/xml/test_cljs.cljs b/src/test/clojurescript/clojure/data/xml/test_cljs.cljs new file mode 100644 index 0000000..c3df468 --- /dev/null +++ b/src/test/clojurescript/clojure/data/xml/test_cljs.cljs @@ -0,0 +1,31 @@ +(ns clojure.data.xml.test-cljs + (:require [cljs.test :as test] + [clojure.data.xml :as xml] + clojure.data.xml.test-cljs-basic + clojure.data.xml.test-cljs-extended)) + +(def ^:dynamic *results*) + +(defmethod test/report [::test/default :end-run-tests] + [m] + (assert (nil? *results*)) + (set! *results* m)) + +(defn ^:export -main-nashorn [] + (set! *print-newline* false) + (set! *print-fn* js/print) + (set! *print-err-fn* js/print) + (binding [*results* nil] + (println "Running Basic Tests") + (test/run-tests 'clojure.data.xml.test-cljs-basic) + (pr-str *results*))) + +(defn ^:export -main [] + (binding [*results* nil] + (println "Running Basic Tests") + (test/run-tests 'clojure.data.xml.test-cljs-basic) + (println "Extending DOM Objects and running again + extended tests") + (xml/extend-dom-as-data!) + (test/testing "with extended native dom" + (test/run-tests 'clojure.data.xml.test-cljs-basic 'clojure.data.xml.test-cljs-extended)) + *results*)) diff --git a/src/test/clojure/clojure/data/xml/test_cljs_basic.cljs b/src/test/clojurescript/clojure/data/xml/test_cljs_basic.cljs similarity index 67% rename from src/test/clojure/clojure/data/xml/test_cljs_basic.cljs rename to src/test/clojurescript/clojure/data/xml/test_cljs_basic.cljs index 23bb22d..fdec66e 100644 --- a/src/test/clojure/clojure/data/xml/test_cljs_basic.cljs +++ b/src/test/clojurescript/clojure/data/xml/test_cljs_basic.cljs @@ -2,7 +2,17 @@ (:require [cljs.test :as test :refer [deftest is are]] [clojure.data.xml :as xml :refer [parse-str emit-str element element-data element-node]] - [clojure.data.xml.node :as node])) + [clojure.data.xml.node :as node] + [clojure.data.xml.js.dom :as dom])) + +(comment + + (= (xml/element :foo) + (xml/parse-str "")) + + (xml/element-data (xml/element-node (xml/element :foo))) + + ) (deftest roundtrips (are [dxml xml] (do (is (= dxml (xml/parse-str xml))) diff --git a/src/test/clojure/clojure/data/xml/test_cljs_extended.cljs b/src/test/clojurescript/clojure/data/xml/test_cljs_extended.cljs similarity index 100% rename from src/test/clojure/clojure/data/xml/test_cljs_extended.cljs rename to src/test/clojurescript/clojure/data/xml/test_cljs_extended.cljs From c9556e3e61fb295d09a6541a117c0d48ebc54a39 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Sat, 3 Dec 2016 18:34:57 +0100 Subject: [PATCH 117/237] use release version of cljc-maven-plugin --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ccb0c9c..431aa87 100644 --- a/pom.xml +++ b/pom.xml @@ -57,7 +57,7 @@ net.bendlas cljc-maven-plugin - 0.1.0-SNAPSHOT + 0.1.1 generate-sources From ce5735dab439fb2b2a88f5726179acac2374d827 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Sat, 3 Dec 2016 19:27:45 +0100 Subject: [PATCH 118/237] clojure 1.8 is required for cljs tests --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 431aa87..e20624f 100644 --- a/pom.xml +++ b/pom.xml @@ -89,8 +89,8 @@ - - 1.9.0-alpha14 + + 1.8.0 From fce30988f6b8a9eab8ea10e91140ba2d412a7897 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Mon, 5 Dec 2016 16:38:35 +0100 Subject: [PATCH 119/237] fix usage of pluginRepositories --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index e20624f..4648ea0 100644 --- a/pom.xml +++ b/pom.xml @@ -93,13 +93,13 @@ 1.8.0 - + - + clojars.org https://clojars.org/repo - - + + scm:git:git@github.com:clojure/data.xml.git From 83347aa5e0def0d77b6f93310e40f6a2d6ab14d5 Mon Sep 17 00:00:00 2001 From: puredanger Date: Mon, 5 Dec 2016 19:06:20 -0600 Subject: [PATCH 120/237] update parent pom for cljc builds --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4648ea0..5dc05d3 100644 --- a/pom.xml +++ b/pom.xml @@ -32,7 +32,7 @@ org.clojure pom.contrib - 0.1.2 + 0.2.0 From 13a060f148476463bcf81b567d5b6471e1abf26b Mon Sep 17 00:00:00 2001 From: puredanger Date: Mon, 5 Dec 2016 19:09:50 -0600 Subject: [PATCH 121/237] bump clojure version to 1.9.0-alpha14 due to spec usage --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5dc05d3..7b558a4 100644 --- a/pom.xml +++ b/pom.xml @@ -90,7 +90,7 @@ - 1.8.0 + 1.9.0-alpha14 From 379b66cdded8f410fb04b5ea217d6b0ca14be3b8 Mon Sep 17 00:00:00 2001 From: puredanger Date: Mon, 5 Dec 2016 19:14:26 -0600 Subject: [PATCH 122/237] add test.check dependency --- pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pom.xml b/pom.xml index 7b558a4..9f233d7 100644 --- a/pom.xml +++ b/pom.xml @@ -36,6 +36,11 @@ + + org.clojure + test.check + 0.9.0 + org.clojure clojurescript From 669f1eb06d0bf69e6e9efb5bb82841c38d244d89 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Wed, 7 Dec 2016 02:45:59 +0100 Subject: [PATCH 123/237] Move spec file to resources This excludes it from compilation --- src/main/{clojure => resources}/clojure/data/xml/spec.cljc | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/main/{clojure => resources}/clojure/data/xml/spec.cljc (100%) diff --git a/src/main/clojure/clojure/data/xml/spec.cljc b/src/main/resources/clojure/data/xml/spec.cljc similarity index 100% rename from src/main/clojure/clojure/data/xml/spec.cljc rename to src/main/resources/clojure/data/xml/spec.cljc From de08046a83338e4d566586867a7c4318671aa96f Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Wed, 7 Dec 2016 02:59:31 +0100 Subject: [PATCH 124/237] Move cljs_testsuite to test/resources jdk1.6 can't compile it, since it doesn't have nashorn --- .../{clojure => resources}/clojure/data/xml/cljs_testsuite.clj | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/test/{clojure => resources}/clojure/data/xml/cljs_testsuite.clj (100%) diff --git a/src/test/clojure/clojure/data/xml/cljs_testsuite.clj b/src/test/resources/clojure/data/xml/cljs_testsuite.clj similarity index 100% rename from src/test/clojure/clojure/data/xml/cljs_testsuite.clj rename to src/test/resources/clojure/data/xml/cljs_testsuite.clj From 4d88ea858ebd1836a55dafa0d3460775b715efe2 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Wed, 7 Dec 2016 03:11:11 +0100 Subject: [PATCH 125/237] set back requirement to clojure 1.5.0 those xmlns directories are only required for clojurescript and trip up clojure 1.5 --- pom.xml | 2 +- .../xmlns/http%3A%2F%2Fwww/w3/org%2F2000%2Fxmlns%2F.cljc | 0 .../xmlns/http%3A%2F%2Fwww/w3/org%2FXML%2F1998%2Fnamespace.cljc | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename src/main/{clojure => clojurescript}/xmlns/http%3A%2F%2Fwww/w3/org%2F2000%2Fxmlns%2F.cljc (100%) rename src/main/{clojure => clojurescript}/xmlns/http%3A%2F%2Fwww/w3/org%2FXML%2F1998%2Fnamespace.cljc (100%) diff --git a/pom.xml b/pom.xml index 9f233d7..36ee186 100644 --- a/pom.xml +++ b/pom.xml @@ -95,7 +95,7 @@ - 1.9.0-alpha14 + 1.5.0 diff --git a/src/main/clojure/xmlns/http%3A%2F%2Fwww/w3/org%2F2000%2Fxmlns%2F.cljc b/src/main/clojurescript/xmlns/http%3A%2F%2Fwww/w3/org%2F2000%2Fxmlns%2F.cljc similarity index 100% rename from src/main/clojure/xmlns/http%3A%2F%2Fwww/w3/org%2F2000%2Fxmlns%2F.cljc rename to src/main/clojurescript/xmlns/http%3A%2F%2Fwww/w3/org%2F2000%2Fxmlns%2F.cljc diff --git a/src/main/clojure/xmlns/http%3A%2F%2Fwww/w3/org%2FXML%2F1998%2Fnamespace.cljc b/src/main/clojurescript/xmlns/http%3A%2F%2Fwww/w3/org%2FXML%2F1998%2Fnamespace.cljc similarity index 100% rename from src/main/clojure/xmlns/http%3A%2F%2Fwww/w3/org%2FXML%2F1998%2Fnamespace.cljc rename to src/main/clojurescript/xmlns/http%3A%2F%2Fwww/w3/org%2FXML%2F1998%2Fnamespace.cljc From 7f21c019666ef8b8bffe3664ffbbf3c1e903b917 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Wed, 7 Dec 2016 03:24:13 +0100 Subject: [PATCH 126/237] scope test.check dependency to test --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index 36ee186..8faa3a4 100644 --- a/pom.xml +++ b/pom.xml @@ -40,6 +40,7 @@ org.clojure test.check 0.9.0 + test org.clojure From a5553b5b1da1c387b5e16431c9cf22a57b00a821 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Wed, 7 Dec 2016 04:54:41 +0100 Subject: [PATCH 127/237] nashorn testsuite: disable code cache --- src/test/resources/clojure/data/xml/cljs_testsuite.clj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/test/resources/clojure/data/xml/cljs_testsuite.clj b/src/test/resources/clojure/data/xml/cljs_testsuite.clj index 5e03240..b96b53c 100644 --- a/src/test/resources/clojure/data/xml/cljs_testsuite.clj +++ b/src/test/resources/clojure/data/xml/cljs_testsuite.clj @@ -34,7 +34,9 @@ (defn run-testsuite! [dir] (System/setProperty "nashorn.persistent.code.cache" "target/nashorn_code_cache") - (let [engine (repl-nh/create-engine)] + (let [engine (repl-nh/create-engine + ;; wait for upgrade to more recent jdk8 + :code-cache false)] (println "INFO" "Running nashorn-repl with" (System/getProperty "nashorn.persistent.code.cache")) (compile-testsuite! dir) (.eval engine (io/reader (io/file dir "tests.reopt.js"))) From 5235ed0ac541096c78931a09db4622234d1c93c2 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Wed, 7 Dec 2016 21:48:10 +0100 Subject: [PATCH 128/237] detect failures in cljs test infrastructure This makes a test fail, if require'ing the cljs testsuite fails on a combination of clojure/jdk >= 1.8/1.8 also revert a5553b5b1da1c387b5e16431c9cf22a57b00a821 because jenkins now has a recent jdk --- src/test/clojure/clojure/data/xml/test_cljs.clj | 16 ++++++++++++---- .../clojure/data/xml/cljs_testsuite.clj | 4 +--- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/test/clojure/clojure/data/xml/test_cljs.clj b/src/test/clojure/clojure/data/xml/test_cljs.clj index de4c781..31de1e7 100644 --- a/src/test/clojure/clojure/data/xml/test_cljs.clj +++ b/src/test/clojure/clojure/data/xml/test_cljs.clj @@ -16,7 +16,15 @@ (require 'clojure.data.xml.cljs-testsuite) (eval '(clojure.data.xml.cljs-testsuite/run-testsuite! "target/cljs-test-nashorn")) (catch Exception e - (println "WARN: clojurescript test suite not available with Clojure" - *clojure-version* (System/getProperty "java.runtime.name") - (System/getProperty "java.vm.version") (System/getProperty "java.runtime.version") - \newline e)))) + (if (or (neg? (compare ((juxt :major :minor) *clojure-version*) + [1 8])) + (neg? (compare (System/getProperty "java.runtime.version") + "1.8"))) + (println "WARN: ignoring cljs testsuite error on clojure < 1.8 or jdk < 1.8" + *clojure-version* (System/getProperty "java.runtime.name") + (System/getProperty "java.vm.version") (System/getProperty "java.runtime.version") + \newline (str e)) + (do (println "ERROR: cljs nashorn test suite should be able to run on clojure >= 1.8 and jdk >= 1.8" + *clojure-version* (System/getProperty "java.runtime.name") + (System/getProperty "java.vm.version") (System/getProperty "java.runtime.version")) + (throw e)))))) diff --git a/src/test/resources/clojure/data/xml/cljs_testsuite.clj b/src/test/resources/clojure/data/xml/cljs_testsuite.clj index b96b53c..5e03240 100644 --- a/src/test/resources/clojure/data/xml/cljs_testsuite.clj +++ b/src/test/resources/clojure/data/xml/cljs_testsuite.clj @@ -34,9 +34,7 @@ (defn run-testsuite! [dir] (System/setProperty "nashorn.persistent.code.cache" "target/nashorn_code_cache") - (let [engine (repl-nh/create-engine - ;; wait for upgrade to more recent jdk8 - :code-cache false)] + (let [engine (repl-nh/create-engine)] (println "INFO" "Running nashorn-repl with" (System/getProperty "nashorn.persistent.code.cache")) (compile-testsuite! dir) (.eval engine (io/reader (io/file dir "tests.reopt.js"))) From 85aeb138e756b835168f686cd866765c3b64e05d Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Tue, 13 Dec 2016 04:50:46 +0100 Subject: [PATCH 129/237] DXML-13 keep whitespace text nodes by default --- .../clojure/clojure/data/xml/jvm/parse.clj | 11 +++-- .../clojure/clojure/data/xml/test_parse.clj | 44 +++++++++++++++++-- 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/src/main/clojure/clojure/data/xml/jvm/parse.clj b/src/main/clojure/clojure/data/xml/jvm/parse.clj index 5c5154f..e15e8b3 100644 --- a/src/main/clojure/clojure/data/xml/jvm/parse.clj +++ b/src/main/clojure/clojure/data/xml/jvm/parse.clj @@ -67,7 +67,7 @@ (defn pull-seq "Creates a seq of events. The XMLStreamConstants/SPACE clause below doesn't seem to be triggered by the JDK StAX parser, but is by others. Leaving in to be more complete." - [^XMLStreamReader sreader {:keys [include-node? location-info] :as opts} ns-envs] + [^XMLStreamReader sreader {:keys [include-node? location-info skip-whitespace] :as opts} ns-envs] (lazy-seq (loop [] (let [location (when location-info @@ -94,10 +94,13 @@ (recur)) XMLStreamConstants/CHARACTERS (if-let [text (and (include-node? :characters) - (not (.isWhiteSpace sreader)) + (not (and skip-whitespace + (.isWhiteSpace sreader))) (.getText sreader))] - (cons (->CharsEvent text) - (pull-seq sreader opts ns-envs)) + (if (zero? (.length ^CharSequence text)) + (recur) + (cons (->CharsEvent text) + (pull-seq sreader opts ns-envs))) (recur)) XMLStreamConstants/COMMENT (if (include-node? :comment) diff --git a/src/test/clojure/clojure/data/xml/test_parse.clj b/src/test/clojure/clojure/data/xml/test_parse.clj index da420c5..caa41ae 100644 --- a/src/test/clojure/clojure/data/xml/test_parse.clj +++ b/src/test/clojure/clojure/data/xml/test_parse.clj @@ -41,9 +41,12 @@ (deftest test-xml-with-whitespace (let [input (str "\n123\n1 2 3\n\n") - expected (element :a {} - (element :b {:with-attr "s p a c e"} "123") - (element :c {} "1 2 3"))] + expected (element :a {} + "\n" + (element :b {:with-attr "s p a c e"} "123") + "\n" + (element :c {} "1 2 3") + "\n\n")] (is (= expected (lazy-parse* input))))) (deftest test-cdata-parse @@ -89,7 +92,40 @@ (is (= 1 (-> input parse-str location-meta :column-number))) (is (= 1 (-> input parse-str :content first location-meta :line-number))) (is (= 4 (-> input parse-str :content first location-meta :column-number))) - (is (= 2 (-> input parse-str :content second location-meta :line-number))) + (is (= 2 (-> input (parse-str :skip-whitespace true) :content second location-meta :line-number))) (is (nil? (-> input (parse-str :location-info false) location-meta))))) + +(deftest test-ignorable-whitespace + ;; FIXME implement clojure.lang.MapEquivalence for records + (clojure.lang.APersistentMap/mapEquals + (parse-str " + + + + + + +]> + + lookupSymbol + + + + + Clojure XML <3 + + + + +") + {:tag :methodCall, :attrs {}, :content + [{:tag :methodName, :attrs {}, :content + ["lookupSymbol"]} + {:tag :params, :attrs {}, :content + [{:tag :param, :attrs {}, :content + [{:tag :value, :attrs {}, :content + [{:tag :string, :attrs {}, :content + ["\n Clojure XML <3 \n "]}]}]}]}]})) From 51b72461227dfa5b7a7e0d685e36ebb78eeb0267 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Tue, 13 Dec 2016 08:05:29 +0100 Subject: [PATCH 130/237] update docs for 0.2.0-alpha1 --- CHANGES.md | 1 + README.md | 15 ++++++++------- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index f9ab071..d990e80 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,6 +4,7 @@ From 0.1.0-beta3 to 0.2.0-alpha1 - Introduce alias-uri - Clojurescript support - data.xml now requires Clojure 1.5.0+ (due to percent-sign in keywords) +- Preserve whitespace by default From 0.1.0-beta2 to 0.1.0-beta3 - Fix emitter to keep non-namespaced xml names out of any set default namespace diff --git a/README.md b/README.md index 7cc85e5..ff21048 100644 --- a/README.md +++ b/README.md @@ -293,11 +293,15 @@ To elide location information, pass `:location-info false` to the parser: The Clojurescript implementation uses the same namespace as the Clojure one `clojure.data.xml`. -### Differences from Clojure implementation +### Native DOM support -data.xml uses native browser dom elements to represent xml. To get back the convenience of treating them as maps, call `(extend-dom-as-data!)`. This extends the native dom node prototypes to Clojurescript collection protocols, such that you can treat them as data.xml parse trees. +data.xml can directly work with native dom nodes. -Of course, the map format is supported for emitting. +- To parse into DOM objects, call parse with `:raw true` +- To use DOM objects like regular persistent maps, call `(extend-dom-as-data!)`. + This extends the native dom node prototypes to Clojurescript collection protocols, such that you can treat them as data.xml parse trees. +- To coerce to native dom use `element-node` +- To coerce to records use `element-data` ### Missing Features, Patches Welcome @@ -317,10 +321,7 @@ Some utilities, like `process/*-xmlns`, `prxml/sexp-as-*`, `indent` aren't yet i Make `extend-dom-as-data!` also support assoc, ... on dom nodes. -#### Data coercions - - - +#### Feel free to pick a [ticket](http://dev.clojure.org/jira/secure/IssueNavigator.jspa?reset=true&jqlQuery=project+%3D+DXML+AND+status+in+%28Open%2C+%22In+Progress%22%2C+Reopened%29) to work on ## License From 322fdc46fa88c7d73a8dd1933fabb1c241427ea8 Mon Sep 17 00:00:00 2001 From: "Hudson @ build.clojure.org" Date: Tue, 13 Dec 2016 04:46:53 -0600 Subject: [PATCH 131/237] [maven-release-plugin] prepare release data.xml-0.2.0-alpha1 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 8faa3a4..9ed84aa 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ 4.0.0 data.xml - 0.2.0-SNAPSHOT + 0.2.0-alpha1 data.xml jar Functions to parse XML into lazy sequences and lazy trees and emit these as text @@ -111,7 +111,7 @@ scm:git:git@github.com:clojure/data.xml.git scm:git:git@github.com:clojure/data.xml.git git@github.com:clojure/data.xml.git - HEAD + data.xml-0.2.0-alpha1
From 5eaee942c2c3ba68486f40c40952c23c7789aa6e Mon Sep 17 00:00:00 2001 From: "Hudson @ build.clojure.org" Date: Tue, 13 Dec 2016 04:46:53 -0600 Subject: [PATCH 132/237] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 9ed84aa..8faa3a4 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ 4.0.0 data.xml - 0.2.0-alpha1 + 0.2.0-SNAPSHOT data.xml jar Functions to parse XML into lazy sequences and lazy trees and emit these as text @@ -111,7 +111,7 @@ scm:git:git@github.com:clojure/data.xml.git scm:git:git@github.com:clojure/data.xml.git git@github.com:clojure/data.xml.git - data.xml-0.2.0-alpha1 + HEAD From c30911e364e13273b75f7125764980e67b2da081 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Thu, 15 Dec 2016 02:28:33 +0100 Subject: [PATCH 133/237] comment pom.xml --- pom.xml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8faa3a4..cf45e24 100644 --- a/pom.xml +++ b/pom.xml @@ -61,6 +61,8 @@ + net.bendlas cljc-maven-plugin 0.1.1 @@ -95,7 +97,8 @@ - + 1.5.0 From e8eee2d46907e3190af45f219cb1c8f9a1eb5193 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Sat, 24 Dec 2016 03:19:28 +0100 Subject: [PATCH 134/237] Clean remnants of 0.1.0 qname handling - qname function now returns canonical (keyword) names - Remove QName defrecord from Clojurescript - Rename canonical-name to as-qname - Remove to-qname --- CHANGES.md | 6 ++ src/main/clojure/clojure/data/xml.clj | 8 +-- src/main/clojure/clojure/data/xml.cljs | 7 +-- src/main/clojure/clojure/data/xml/js/dom.cljs | 7 +-- .../clojure/clojure/data/xml/js/name.cljs | 29 +++------ .../clojure/clojure/data/xml/jvm/name.clj | 10 ++- .../clojure/clojure/data/xml/jvm/parse.clj | 15 +++-- src/main/clojure/clojure/data/xml/name.cljc | 62 ++++++++++++------- src/main/clojure/clojure/data/xml/node.cljc | 6 +- src/main/resources/clojure/data/xml/spec.cljc | 4 +- .../clojure/clojure/data/xml/test_emit.clj | 6 +- .../clojure/clojure/data/xml/test_names.clj | 4 +- 12 files changed, 86 insertions(+), 78 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index d990e80..07b7071 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,9 @@ +From 0.2.0-alpha1 to 0.2.0-alpha2 +- qname function now returns canonical (keyword) names +- Remove QName defrecord from Clojurescript +- Rename canonical-name to as-qname +- Remove to-qname + From 0.1.0-beta3 to 0.2.0-alpha1 - Define uniform mapping of xml namespaces to clojure namespaces via percent-encoding - Remove declare-ns and alias-ns diff --git a/src/main/clojure/clojure/data/xml.clj b/src/main/clojure/clojure/data/xml.clj index bd1a8ad..de66512 100644 --- a/src/main/clojure/clojure/data/xml.clj +++ b/src/main/clojure/clojure/data/xml.clj @@ -34,14 +34,10 @@ (export-api node/element* node/element node/cdata node/xml-comment prxml/sexp-as-element prxml/sexps-as-fragment event/element-nss name/alias-uri name/parse-qname name/qname-uri - name/qname-local name/qname name/to-qname name/uri-symbol name/symbol-uri + name/qname-local name/qname name/as-qname name/uri-symbol name/symbol-uri + name/uri-file name/print-uri-file-command! process/find-xmlns process/aggregate-xmlns) -(defn canonical-name - "Put (q)name into canonical form as per ns-env" - [n] - (name/canonical-name (qname-uri n) (qname-local n) "")) - (defn event-seq "Parses the XML InputSource source using a pull-parser. Returns a lazy sequence of Event records. Accepts key pairs diff --git a/src/main/clojure/clojure/data/xml.cljs b/src/main/clojure/clojure/data/xml.cljs index d9ff4a5..b4e768b 100644 --- a/src/main/clojure/clojure/data/xml.cljs +++ b/src/main/clojure/clojure/data/xml.cljs @@ -8,15 +8,10 @@ [clojure.data.xml.protocols :refer [AsQName]])) (export-api - name/parse-qname name/qname-uri name/qname-local name/qname name/to-qname name/uri-symbol name/symbol-uri + name/parse-qname name/qname-uri name/qname-local name/qname name/as-qname name/uri-symbol name/symbol-uri node/element* node/element node/cdata node/xml-comment dom/extend-dom-as-data! dom/element-node dom/element-data) -(defn canonical-name - "Put (q)name into canonical form as per ns-env" - [n] - (name/canonical-name (qname-uri n) (qname-local n) "")) - ;;;; ## TODO event-seq ;; This probably won't happen due to js' non-blocking semantics ;; Instead, for clojurescript, the machinery around event-seq could be implemented diff --git a/src/main/clojure/clojure/data/xml/js/dom.cljs b/src/main/clojure/clojure/data/xml/js/dom.cljs index 5851316..e080424 100644 --- a/src/main/clojure/clojure/data/xml/js/dom.cljs +++ b/src/main/clojure/clojure/data/xml/js/dom.cljs @@ -1,6 +1,6 @@ (ns clojure.data.xml.js.dom (:require - [clojure.data.xml.name :refer [qname-uri qname-local canonical-name xmlns-uri]] + [clojure.data.xml.name :refer [qname-uri qname-local qname xmlns-uri]] [clojure.data.xml.node :as node])) (def doc @@ -103,9 +103,8 @@ ;; ## -> DATA (defn- dom-element-tag [el] - (canonical-name (.-namespaceURI el) - (.-localName el) - "")) + (qname (.-namespaceURI el) + (.-localName el))) (defn- xmlns-attr? [a] (identical? xmlns-uri (.-namespaceURI a))) diff --git a/src/main/clojure/clojure/data/xml/js/name.cljs b/src/main/clojure/clojure/data/xml/js/name.cljs index 0841017..22c85bc 100644 --- a/src/main/clojure/clojure/data/xml/js/name.cljs +++ b/src/main/clojure/clojure/data/xml/js/name.cljs @@ -2,29 +2,20 @@ (:require [clojure.data.xml.protocols :refer [AsQName qname-uri qname-local]] [clojure.string :as str])) -(defrecord QName [uri local prefix] - AsQName - (qname-local [_] local) - (qname-uri [_] uri)) - -(specify! (.-prototype QName) - IEquiv - (-equiv [this other] - (and (instance? QName other) - (= (.-local this) (.-local other)) - (= (.-uri this) (.-uri other))))) - -(defn make-qname [uri name prefix] - (->QName uri name prefix)) - (def parse-qname - (memoize - (fn [s] - (let [[_ ns-uri local] (re-matches #"(?:\{([^}]+)\})?([^{]*)" s)] - (make-qname (or ns-uri "") local ""))))) + (memoize (partial re-matches #"(?:\{([^}]+)\})?([^{]*)"))) (defn decode-uri [ns] (js/decodeURIComponent ns)) (defn encode-uri [uri] (js/encodeURIComponent uri)) + +(extend-protocol AsQName + string + (qname-local [s] + (let [[_ _ local] (parse-qname s)] + local)) + (qname-uri [s] + (let [[_ uri _] (parse-qname s)] + uri))) diff --git a/src/main/clojure/clojure/data/xml/jvm/name.clj b/src/main/clojure/clojure/data/xml/jvm/name.clj index ba2b424..3f71c53 100644 --- a/src/main/clojure/clojure/data/xml/jvm/name.clj +++ b/src/main/clojure/clojure/data/xml/jvm/name.clj @@ -19,14 +19,18 @@ (qname-local [qname] (.getLocalPart qname)) (qname-uri [qname] (.getNamespaceURI qname))) -(def parse-qname +(def ^QName parse-qname (memoize (fn [s] ;; TODO weakly memoize this? (QName/valueOf s)))) -(definline make-qname [uri name prefix] - `(QName. ~uri ~name ~prefix)) +(extend-protocol AsQName + String + (qname-local [s] + (.getLocalPart (parse-qname s))) + (qname-uri [s] + (.getNamespaceURI (parse-qname s)))) (definline decode-uri [ns] `(URLDecoder/decode ~ns "UTF-8")) diff --git a/src/main/clojure/clojure/data/xml/jvm/parse.clj b/src/main/clojure/clojure/data/xml/jvm/parse.clj index e15e8b3..dba8035 100644 --- a/src/main/clojure/clojure/data/xml/jvm/parse.clj +++ b/src/main/clojure/clojure/data/xml/jvm/parse.clj @@ -14,7 +14,7 @@ [clojure.data.xml.impl :refer [static-case]] [clojure.data.xml.name :refer - [canonical-name]]) + [qname]]) (:import (javax.xml.stream XMLInputFactory XMLStreamReader XMLStreamConstants))) @@ -38,9 +38,9 @@ (defn- attr-hash [^XMLStreamReader sreader] (persistent! (reduce (fn [tr i] - (assoc! tr (canonical-name (.getAttributeNamespace sreader i) - (.getAttributeLocalName sreader i) - (.getAttributePrefix sreader i)) + (assoc! tr (qname (.getAttributeNamespace sreader i) + (.getAttributeLocalName sreader i) + (.getAttributePrefix sreader i)) (.getAttributeValue sreader i))) (transient {}) (range (.getAttributeCount sreader))))) @@ -74,13 +74,12 @@ (location-hash sreader))] (static-case (.next sreader) - ; condp == (.next sreader) XMLStreamConstants/START_ELEMENT (if (include-node? :element) (let [ns-env (nss-hash sreader (or (first ns-envs) {}))] - (cons (->StartElementEvent (canonical-name (.getNamespaceURI sreader) - (.getLocalName sreader) - (.getPrefix sreader)) + (cons (->StartElementEvent (qname (.getNamespaceURI sreader) + (.getLocalName sreader) + (.getPrefix sreader)) (attr-hash sreader) ns-env location) diff --git a/src/main/clojure/clojure/data/xml/name.cljc b/src/main/clojure/clojure/data/xml/name.cljc index dcd9287..dcac3f3 100644 --- a/src/main/clojure/clojure/data/xml/name.cljc +++ b/src/main/clojure/clojure/data/xml/name.cljc @@ -21,8 +21,8 @@ (:import (goog.string StringBuffer))])) (export-api - #?@(:clj [jvm/parse-qname jvm/make-qname jvm/encode-uri jvm/decode-uri] - :cljs [jsn/parse-qname jsn/make-qname jsn/encode-uri jsn/decode-uri])) + #?@(:clj [jvm/parse-qname jvm/encode-uri jvm/decode-uri] + :cljs [jsn/parse-qname jsn/encode-uri jsn/decode-uri])) ;; protocol functions can be redefined by extend-*, so we wrap ;; protocols/qname-uri protocols/qname-local within regular fns @@ -47,9 +47,11 @@ (protocols/qname-local v)) (defn qname - ([name] (make-qname "" name "")) - ([uri name] (make-qname (or uri "") name "")) - ([uri name prefix] (make-qname (or uri "") name (or prefix "")))) + ([local] (qname "" local)) + ([uri local] (keyword (when-not (str/blank? uri) + (encode-uri (str "xmlns." uri))) + local)) + ([uri local prefix] (qname uri local))) ;; The empty string shall be equal to nil for xml names (defn namespaced? [qn] @@ -76,28 +78,44 @@ xmlns-uri (throw (ex-info "Keyword ns is not an xmlns. Needs to be in the form :xmlns./" {:kw kw})))) - "")) - #?(:clj String :cljs string) - (qname-local [s] (qname-local (parse-qname s))) - (qname-uri [s] (qname-uri (parse-qname s)))) - -(defn canonical-name - ([local] (canonical-name "" local "")) - ([uri local] (canonical-name uri local "")) - ([uri local prefix] - (keyword (when-not (str/blank? uri) - (encode-uri (str "xmlns." uri))) - local))) - -(defn to-qname [n] - (make-qname (or (qname-uri n) "") (qname-local n) "")) + ""))) + +(defn as-qname [n] + (qname (qname-uri n) (qname-local n))) + +(defn uri-file + "Dummy file name for :require'ing xmlns uri" + [uri] + (str (str/replace (name (uri-symbol uri)) + "." "/") + ".cljc")) + +(defn print-uri-file-command! + "Shell command to create a dummy file for xmlns. Execute from a source root." + [uri] + (println "echo \"(ns" (str (uri-symbol uri) ")\" >") (uri-file uri))) #?(:clj (defn alias-uri - "Define a clojure namespace alias for xmlns uri. + "Define a Clojure namespace aliases for xmlns uris. + + This sets up the current namespace for reading qnames denoted with + Clojure's ::alias/keywords reader feature. + + ## Example (alias-uri :D \"DAV:\") - {:tag ::D/propfind}" + ; similar in effect to + ;; (require '[xmlns.DAV%3A :as D]) + ; but required namespace is auto-created + ; henceforth, shorthand keywords can be used + {:tag ::D/propfind} + ; ::D/propfind will be expanded to :xmlns.DAV%3A/propfind + ; in the current namespace by the reader + + ## Clojurescript support + Currently, namespaces can't be auto-created in Clojurescript. + Dummy files for aliased uris have to exist. Have a look at `uri-file` and `print-uri-file-command!` to create those." {:arglists '([& {:as alias-nss}])} [& ans] (loop [[a n & rst :as ans] ans] diff --git a/src/main/clojure/clojure/data/xml/node.cljc b/src/main/clojure/clojure/data/xml/node.cljc index a2c4962..c25e325 100644 --- a/src/main/clojure/clojure/data/xml/node.cljc +++ b/src/main/clojure/clojure/data/xml/node.cljc @@ -9,17 +9,17 @@ (ns clojure.data.xml.node "Data types for xml nodes: Element, CData and Comment" {:author "Herwig Hochleitner"} - (:require [clojure.data.xml.name :refer [to-qname]])) + (:require [clojure.data.xml.name :refer [as-qname]])) ;; Parsed data format ;; Represents a node of an XML tree (defrecord Element [tag attrs content] Object (toString [_] - (let [qname (to-qname tag)] + (let [qname (as-qname tag)] (apply str (concat ["<" qname] (mapcat (fn [[n a]] - [" " (to-qname n) "=" (pr-str a)]) + [" " (as-qname n) "=" (pr-str a)]) attrs) (if (seq content) (concat [">"] content [""]) diff --git a/src/main/resources/clojure/data/xml/spec.cljc b/src/main/resources/clojure/data/xml/spec.cljc index ee314af..5824d70 100644 --- a/src/main/resources/clojure/data/xml/spec.cljc +++ b/src/main/resources/clojure/data/xml/spec.cljc @@ -18,7 +18,7 @@ ::s/invalid))) (fn [{:keys [uri local] :as arg}] (.log js/console arg) - (name/canonical-name uri local ""))) + (name/qname uri local))) #(not (str/blank? (:local %))))) (s/def ::name/qname @@ -30,7 +30,7 @@ :str (s/and string? ::qname-conformer))) (s/with-gen #(gen/fmap (fn [[uri local]] - (name/canonical-name uri local)) + (name/qname uri local)) (gen/tuple (gen/fmap (fn [s] (when-not (str/blank? s) (str "urn:" s))) (gen/string-alphanumeric)) diff --git a/src/test/clojure/clojure/data/xml/test_emit.clj b/src/test/clojure/clojure/data/xml/test_emit.clj index 48ebdce..396f842 100644 --- a/src/test/clojure/clojure/data/xml/test_emit.clj +++ b/src/test/clojure/clojure/data/xml/test_emit.clj @@ -180,9 +180,9 @@ (emit-str (event-seq (java.io.StringReader. "123") {}))))) (deftest test-sibling-xmlns - (let [el (element (canonical-name "{NS1}top") {} - (element (canonical-name "{NS2}foo")) - (element (canonical-name "{NS2}bar")))] + (let [el (element (as-qname "{NS1}top") {} + (element (as-qname "{NS2}foo")) + (element (as-qname "{NS2}bar")))] (is (= (parse-str (emit-str el)) el)))) (deftest test-default-xmlns diff --git a/src/test/clojure/clojure/data/xml/test_names.clj b/src/test/clojure/clojure/data/xml/test_names.clj index ed241fb..1cca97e 100644 --- a/src/test/clojure/clojure/data/xml/test_names.clj +++ b/src/test/clojure/clojure/data/xml/test_names.clj @@ -13,7 +13,7 @@ (is (= vals [(qname-uri v) (qname-local v)]) (str "Interpreted QName: " (pr-str v))))) ["" "name"] ["name" :name (parse-qname "name")] - ["uri-u:" "name"] [::U/name "{uri-u:}name" (parse-qname "{uri-u:}name") (canonical-name "{uri-u:}name")] + ["uri-u:" "name"] [::U/name "{uri-u:}name" (parse-qname "{uri-u:}name") (as-qname "{uri-u:}name")] ["uri-v:" "vname"] [::V/vname "{uri-v:}vname" (parse-qname "{uri-v:}vname")] ["uri-w:" "wname"] [::W/wname "{uri-w:}wname" (parse-qname "{uri-w:}wname")] ;; ["http://www.w3.org/XML/1998/namespace" "name"] [:xml/name] @@ -33,7 +33,7 @@ (element ::D/nresults nil "100")))) (deftest qnames - (is (= (qname "foo") (to-qname :foo)))) + (is (= (qname "foo") (as-qname :foo)))) (deftest test-gen-prefix (are [node] (= (parse-str (emit-str node)) node) From adce289889d19526b77b7842cdf609e7e472da6c Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Sun, 25 Dec 2016 02:50:26 +0100 Subject: [PATCH 135/237] DXML-38 Implement MapEquivalence for xml elements We have to reimplement most of defrecord in a deftype, in order to have proper equivalence to element maps. --- CHANGES.md | 1 + src/main/clojure/clojure/data/xml/node.cljc | 165 ++++++++++++++++-- .../clojure/data/xml/test_entities.clj | 12 +- .../clojure/clojure/data/xml/test_equiv.cljc | 11 ++ .../clojure/data/xml/test_cljs.cljs | 10 +- 5 files changed, 179 insertions(+), 20 deletions(-) create mode 100644 src/test/clojure/clojure/data/xml/test_equiv.cljc diff --git a/CHANGES.md b/CHANGES.md index 07b7071..f816fb1 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,6 +3,7 @@ From 0.2.0-alpha1 to 0.2.0-alpha2 - Remove QName defrecord from Clojurescript - Rename canonical-name to as-qname - Remove to-qname +- xml nodes now implement map equality From 0.1.0-beta3 to 0.2.0-alpha1 - Define uniform mapping of xml namespaces to clojure namespaces via percent-encoding diff --git a/src/main/clojure/clojure/data/xml/node.cljc b/src/main/clojure/clojure/data/xml/node.cljc index c25e325..eaf5b3a 100644 --- a/src/main/clojure/clojure/data/xml/node.cljc +++ b/src/main/clojure/clojure/data/xml/node.cljc @@ -9,11 +9,57 @@ (ns clojure.data.xml.node "Data types for xml nodes: Element, CData and Comment" {:author "Herwig Hochleitner"} - (:require [clojure.data.xml.name :refer [as-qname]])) + (:require [clojure.data.xml.name :refer [as-qname]]) + #?(:clj (:import (clojure.lang IHashEq IObj ILookup IKeywordLookup Counted + Associative Seqable IPersistentMap + APersistentMap RecordIterator RT + MapEquivalence) + (java.io Serializable Writer) + (java.util Map)))) + +#? +(:clj + ;; recreate cljs' caching-hash macro + (defmacro caching-hash [this hash-fn hash-field] + `(do (when-not ~hash-field + (set! ~hash-field (~hash-fn ~this))) + ~hash-field))) ;; Parsed data format ;; Represents a node of an XML tree -(defrecord Element [tag attrs content] + +;; We implement a custom deftype for elements +;; it is similar to (defrecord Element [tag attrs content]) +;; but we override its hash and equality to be compatible with +;; clojure's hash-maps +;; see http://dev.clojure.org/jira/browse/CLJ-2084 +;; also, elements don't have an extmap and degrade to hash-maps also +;; when assoc'ing unknown keys + +(deftype Element [tag attrs content meta + #?(:clj ^:volatile-mutable hash + :cljs ^:mutable hash)] + + ;; serializing/cloning, hashing, equality, iteration + + #?@ + (:clj + [Serializable + MapEquivalence + IHashEq + (hasheq [this] (caching-hash this APersistentMap/mapHasheq hash)) + Iterable + (iterator [this] (RecordIterator. this [:tag :attrs :content] (RT/iter nil)))] + :cljs + [ICloneable + (-clone [_] (Element. tag attrs content meta hash)) + IHash + (-hash [this] (caching-hash this hash-unordered-coll hash)) + IEquiv + (-equiv [this other] (or (identical? this other) + ^boolean (js/cljs.core.equiv_map this other))) + IIterable + (-iterator [this] (RecordIter. 0 this 3 [:tag :attrs :content] (nil-iter)))]) Object (toString [_] (let [qname (as-qname tag)] @@ -23,22 +69,119 @@ attrs) (if (seq content) (concat [">"] content [""]) - ["/>"])))))) + ["/>"]))))) + #?@(:clj + [(hashCode [this] (caching-hash this APersistentMap/mapHash hash)) + (equals [this other] (APersistentMap/mapEquals this other)) + IPersistentMap + (equiv [this other] (APersistentMap/mapEquals this other))]) + + ;; Main collection interfaces, that are included in IPersistentMap, + ;; but are separate protocols in cljs + + #?(:cljs ILookup) + (#?(:clj valAt :cljs -lookup) [this k] + (#?(:clj .valAt :cljs -lookup) + this k nil)) + (#?(:clj valAt :cljs -lookup) [this k nf] + (case k + :tag tag + :attrs attrs + :content content + nf)) + #?(:cljs ICounted) + (#?(:clj count :cljs -count) [this] 3) + #?(:cljs ICollection) + (#?(:clj cons :cljs -conj) [this entry] + (conj (with-meta {:tag tag :attrs attrs :content content} meta) + entry)) + #?(:cljs IAssociative) + (#?(:clj assoc :cljs -assoc) [this k v] + (case k + :tag (Element. v attrs content meta nil) + :attrs (Element. tag v content meta nil) + :content (Element. tag attrs v meta nil) + (with-meta {:tag tag :attrs attrs :content content k v} meta))) + #?(:cljs IMap) + (#?(:clj without :cljs -dissoc) [this k] + (with-meta + (case k + :tag {:attrs attrs :content content} + :attrs {:tag tag :content content} + :content {:tag tag :attrs attrs} + this) + meta)) + #?@(:cljs + [ISeqable + (-seq [this] + (seq [[:tag tag] [:attrs attrs] [:content content]]))] + :clj + [(seq [this] (iterator-seq (.iterator this)))]) + + ;; j.u.Map and included interfaces + #?@(:clj + [Map + (entrySet [this] (set this)) + (values [this] (vals this)) + (keySet [this] (set (keys this))) + (get [this k] (.valAt this k)) + (containsKey [this k] (case k (:tag :attrs :content) true false)) + (containsValue [this v] (boolean (some #{v} (vals this)))) + (isEmpty [this] false) + (size [this] 3)]) + + ;; Metadata interface + + #?(:clj IObj :cljs IMeta) + (#?(:clj meta :cljs -meta) [this] meta) + #?(:cljs IWithMeta) + (#?(:clj withMeta :cljs -with-meta) [this next-meta] + (Element. tag attrs content next-meta hash)) + + ;; cljs printing is protocol-based + + #?@ + (:cljs + [IPrintWithWriter + (-pr-writer [this writer opts] + (-write writer "#xml/element{:tag ") + (-pr-writer tag writer opts) + (when-not (empty? attrs) + (-write writer ", :attrs ") + (-pr-writer attrs writer opts)) + (when-not (empty? content) + (-write writer ", :content ") + (pr-sequential-writer writer -pr-writer "[" " " "]" opts content)) + (-write writer "}"))])) + +;; clj printing is a multimethod + +#? +(:clj + (defmethod print-method Element [{:keys [tag attrs content]} ^Writer writer] + (.write writer "#xml/element{:tag ") + (print-method tag writer) + (when-not (empty? attrs) + (.write writer ", :attrs ") + (print-method attrs writer)) + (when-not (empty? content) + (.write writer ", :content [") + (print-method (first content) writer) + (doseq [c (next content)] + (.write writer " ") + (print-method c writer)) + (.write writer "]")) + (.write writer "}"))) + (defrecord CData [content]) (defrecord Comment [content]) -#?(:cljs ;; http://dev.clojure.org/jira/browse/CLJS-1859 - (extend-type Element - IEquiv - (-equiv [el o] - (js/cljs.core.equiv_map el o)))) - (defn element* "Create an xml element from a content collection and optional metadata" ([tag attrs content meta] (Element. tag (or attrs {}) (remove nil? content) meta nil)) ([tag attrs content] - (Element. tag (or attrs {}) (remove nil? content)))) + (Element. tag (or attrs {}) (remove nil? content) nil nil))) #?(:clj ;; Compiler macro for inlining the two constructors @@ -47,7 +190,7 @@ ([tag attrs content meta] `(Element. ~tag (or ~attrs {}) (remove nil? ~content) ~meta nil)) ([tag attrs content] - `(Element. ~tag (or ~attrs {}) (remove nil? ~content)))))) + `(Element. ~tag (or ~attrs {}) (remove nil? ~content) nil nil))))) (defn element "Create an xml Element from content varargs" diff --git a/src/test/clojure/clojure/data/xml/test_entities.clj b/src/test/clojure/clojure/data/xml/test_entities.clj index a6b8353..2bb5db1 100644 --- a/src/test/clojure/clojure/data/xml/test_entities.clj +++ b/src/test/clojure/clojure/data/xml/test_entities.clj @@ -35,15 +35,15 @@ (deftest prevent-xxe-by-default (testing "To prevent XXE attacks, exernal entities by default resolve to nil" (let [parsed (parse-vulnerable-file) - expected #clojure.data.xml.node.Element{:tag :foo - :attrs {} - :content ()}] + expected {:tag :foo + :attrs {} + :content ()}] (is (= expected parsed))))) (deftest allow-external-entities-if-required (testing "If explicitly enabled, external entities are property resolved" (let [parsed (parse-vulnerable-file :supporting-external-entities true) - expected #clojure.data.xml.node.Element{:tag :foo - :attrs {} - :content ("root_password\n")}] + expected {:tag :foo + :attrs {} + :content ["root_password\n"]}] (is (= expected parsed))))) diff --git a/src/test/clojure/clojure/data/xml/test_equiv.cljc b/src/test/clojure/clojure/data/xml/test_equiv.cljc new file mode 100644 index 0000000..555da6d --- /dev/null +++ b/src/test/clojure/clojure/data/xml/test_equiv.cljc @@ -0,0 +1,11 @@ +(ns clojure.data.xml.test-equiv + (:require [clojure.data.xml :refer [element qname]] + [clojure.test :refer [deftest is are testing]])) + +(deftest test-node-equivalence + (are [repr1 repr2] (and (is (= repr1 repr2)) + (is (= (hash repr1) (hash repr2)))) + (element :foo) {:tag :foo :attrs {} :content []} + (element (qname "DAV:" "foo")) {:tag (qname "DAV:" "foo") :attrs {} :content []} + (element :foo {:a "b"}) {:tag :foo :attrs {:a "b"} :content []} + (element :foo {:a "b"} "a" "b") {:tag :foo :attrs {:a "b"} :content ["a" "b"]})) diff --git a/src/test/clojurescript/clojure/data/xml/test_cljs.cljs b/src/test/clojurescript/clojure/data/xml/test_cljs.cljs index c3df468..dc956ac 100644 --- a/src/test/clojurescript/clojure/data/xml/test_cljs.cljs +++ b/src/test/clojurescript/clojure/data/xml/test_cljs.cljs @@ -2,7 +2,8 @@ (:require [cljs.test :as test] [clojure.data.xml :as xml] clojure.data.xml.test-cljs-basic - clojure.data.xml.test-cljs-extended)) + clojure.data.xml.test-cljs-extended + clojure.data.xml.test-equiv)) (def ^:dynamic *results*) @@ -17,7 +18,8 @@ (set! *print-err-fn* js/print) (binding [*results* nil] (println "Running Basic Tests") - (test/run-tests 'clojure.data.xml.test-cljs-basic) + (test/run-tests 'clojure.data.xml.test-cljs-basic + 'clojure.data.xml.test-equiv) (pr-str *results*))) (defn ^:export -main [] @@ -27,5 +29,7 @@ (println "Extending DOM Objects and running again + extended tests") (xml/extend-dom-as-data!) (test/testing "with extended native dom" - (test/run-tests 'clojure.data.xml.test-cljs-basic 'clojure.data.xml.test-cljs-extended)) + (test/run-tests 'clojure.data.xml.test-cljs-basic + 'clojure.data.xml.test-cljs-extended + 'clojure.data.xml.test-equiv)) *results*)) From 6a9b32f9c5e90d552c51056e78ba4d8d0b00b009 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Sun, 25 Dec 2016 04:19:40 +0100 Subject: [PATCH 136/237] Implement #xml/element reader tag Unfortunately, this necessitates removal of hash caching, because deftypes with mutable fields cannot be compiled: CLJ-2029 --- src/main/clojure/clojure/data/xml/node.cljc | 47 ++++++++++--------- src/main/clojure/data_readers.clj | 1 - src/main/clojure/data_readers.cljc | 2 + .../clojure/clojure/data/xml/test_equiv.cljc | 4 +- 4 files changed, 29 insertions(+), 25 deletions(-) delete mode 100644 src/main/clojure/data_readers.clj create mode 100644 src/main/clojure/data_readers.cljc diff --git a/src/main/clojure/clojure/data/xml/node.cljc b/src/main/clojure/clojure/data/xml/node.cljc index eaf5b3a..cf00f5a 100644 --- a/src/main/clojure/clojure/data/xml/node.cljc +++ b/src/main/clojure/clojure/data/xml/node.cljc @@ -17,14 +17,6 @@ (java.io Serializable Writer) (java.util Map)))) -#? -(:clj - ;; recreate cljs' caching-hash macro - (defmacro caching-hash [this hash-fn hash-field] - `(do (when-not ~hash-field - (set! ~hash-field (~hash-fn ~this))) - ~hash-field))) - ;; Parsed data format ;; Represents a node of an XML tree @@ -36,9 +28,9 @@ ;; also, elements don't have an extmap and degrade to hash-maps also ;; when assoc'ing unknown keys -(deftype Element [tag attrs content meta - #?(:clj ^:volatile-mutable hash - :cljs ^:mutable hash)] +;; FIXME hash caching cannot be used: http://dev.clojure.org/jira/browse/CLJ-2092 + +(deftype Element [tag attrs content meta] ;; serializing/cloning, hashing, equality, iteration @@ -47,14 +39,14 @@ [Serializable MapEquivalence IHashEq - (hasheq [this] (caching-hash this APersistentMap/mapHasheq hash)) + (hasheq [this] (APersistentMap/mapHasheq this)) Iterable (iterator [this] (RecordIterator. this [:tag :attrs :content] (RT/iter nil)))] :cljs [ICloneable - (-clone [_] (Element. tag attrs content meta hash)) + (-clone [_] (Element. tag attrs content meta)) IHash - (-hash [this] (caching-hash this hash-unordered-coll hash)) + (-hash [this] (hash-unordered-coll this)) IEquiv (-equiv [this other] (or (identical? this other) ^boolean (js/cljs.core.equiv_map this other))) @@ -71,7 +63,7 @@ (concat [">"] content [""]) ["/>"]))))) #?@(:clj - [(hashCode [this] (caching-hash this APersistentMap/mapHash hash)) + [(hashCode [this] (APersistentMap/mapHash this)) (equals [this other] (APersistentMap/mapEquals this other)) IPersistentMap (equiv [this other] (APersistentMap/mapEquals this other))]) @@ -98,9 +90,9 @@ #?(:cljs IAssociative) (#?(:clj assoc :cljs -assoc) [this k v] (case k - :tag (Element. v attrs content meta nil) - :attrs (Element. tag v content meta nil) - :content (Element. tag attrs v meta nil) + :tag (Element. v attrs content meta) + :attrs (Element. tag v content meta) + :content (Element. tag attrs v meta) (with-meta {:tag tag :attrs attrs :content content k v} meta))) #?(:cljs IMap) (#?(:clj without :cljs -dissoc) [this k] @@ -136,7 +128,7 @@ (#?(:clj meta :cljs -meta) [this] meta) #?(:cljs IWithMeta) (#?(:clj withMeta :cljs -with-meta) [this next-meta] - (Element. tag attrs content next-meta hash)) + (Element. tag attrs content next-meta)) ;; cljs printing is protocol-based @@ -179,18 +171,18 @@ (defn element* "Create an xml element from a content collection and optional metadata" ([tag attrs content meta] - (Element. tag (or attrs {}) (remove nil? content) meta nil)) + (Element. tag (or attrs {}) (remove nil? content) meta)) ([tag attrs content] - (Element. tag (or attrs {}) (remove nil? content) nil nil))) + (Element. tag (or attrs {}) (remove nil? content) nil))) #?(:clj ;; Compiler macro for inlining the two constructors (alter-meta! #'element* assoc :inline (fn ([tag attrs content meta] - `(Element. ~tag (or ~attrs {}) (remove nil? ~content) ~meta nil)) + `(Element. ~tag (or ~attrs {}) (remove nil? ~content) ~meta)) ([tag attrs content] - `(Element. ~tag (or ~attrs {}) (remove nil? ~content) nil nil))))) + `(Element. ~tag (or ~attrs {}) (remove nil? ~content) nil))))) (defn element "Create an xml Element from content varargs" @@ -207,3 +199,12 @@ "Create a Comment node" [content] (Comment. content)) + +(defn map->Element [{:keys [tag attrs content] :as el}] + (element* tag attrs content (meta el))) + +(defn tagged-element [el] + (cond (map? el) (map->Element el) + ;; TODO support hiccup syntax + :else (throw (ex-info "Unsupported element representation" + {:element el})))) diff --git a/src/main/clojure/data_readers.clj b/src/main/clojure/data_readers.clj deleted file mode 100644 index b48496b..0000000 --- a/src/main/clojure/data_readers.clj +++ /dev/null @@ -1 +0,0 @@ -{xml/ns clojure.data.xml.name/uri-symbol} diff --git a/src/main/clojure/data_readers.cljc b/src/main/clojure/data_readers.cljc new file mode 100644 index 0000000..f4db79a --- /dev/null +++ b/src/main/clojure/data_readers.cljc @@ -0,0 +1,2 @@ +{xml/ns clojure.data.xml.name/uri-symbol + xml/element clojure.data.xml.node/tagged-element} diff --git a/src/test/clojure/clojure/data/xml/test_equiv.cljc b/src/test/clojure/clojure/data/xml/test_equiv.cljc index 555da6d..cdfdc72 100644 --- a/src/test/clojure/clojure/data/xml/test_equiv.cljc +++ b/src/test/clojure/clojure/data/xml/test_equiv.cljc @@ -8,4 +8,6 @@ (element :foo) {:tag :foo :attrs {} :content []} (element (qname "DAV:" "foo")) {:tag (qname "DAV:" "foo") :attrs {} :content []} (element :foo {:a "b"}) {:tag :foo :attrs {:a "b"} :content []} - (element :foo {:a "b"} "a" "b") {:tag :foo :attrs {:a "b"} :content ["a" "b"]})) + (element :foo {:a "b"} "a" "b") {:tag :foo :attrs {:a "b"} :content ["a" "b"]} + #?@(:clj ;; wait for https://github.com/clojure/clojurescript/commit/9484a134bdf039c10ec3c26c8aaa3acd0dcd9875 + [#xml/element {:tag :foo :content ["C"]} {:tag :foo :attrs {} :content ["C"]}]))) From 59885b2eeacef967c11525b6425296a8fed7734d Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Sun, 25 Dec 2016 05:29:16 +0100 Subject: [PATCH 137/237] Fix for clojure 1.5 --- pom.xml | 8 +++++++- src/main/clojure/clojure/data/xml/node.cljc | 17 +++++++++++++---- .../clojure/clojure/data/xml/test_equiv.cljc | 4 +--- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index cf45e24..ec074d4 100644 --- a/pom.xml +++ b/pom.xml @@ -65,12 +65,18 @@ hence enabling usage of .cljc while supporting clojure < 1.7.0 --> net.bendlas cljc-maven-plugin - 0.1.1 + 0.1.2 + split-compile generate-sources split + + split-compile-tests + generate-test-sources + split-tests + diff --git a/src/main/clojure/clojure/data/xml/node.cljc b/src/main/clojure/clojure/data/xml/node.cljc index cf00f5a..680151c 100644 --- a/src/main/clojure/clojure/data/xml/node.cljc +++ b/src/main/clojure/clojure/data/xml/node.cljc @@ -12,10 +12,9 @@ (:require [clojure.data.xml.name :refer [as-qname]]) #?(:clj (:import (clojure.lang IHashEq IObj ILookup IKeywordLookup Counted Associative Seqable IPersistentMap - APersistentMap RecordIterator RT - MapEquivalence) + APersistentMap RT MapEquivalence MapEntry) (java.io Serializable Writer) - (java.util Map)))) + (java.util Map Iterator)))) ;; Parsed data format ;; Represents a node of an XML tree @@ -30,6 +29,16 @@ ;; FIXME hash caching cannot be used: http://dev.clojure.org/jira/browse/CLJ-2092 +#? +(:clj + (deftype ElementIterator [el ^:volatile-mutable fields] + Iterator + (hasNext [_] (boolean (seq fields))) + (next [_] + (let [f (first fields)] + (set! fields (next fields)) + (MapEntry. f (get el f)))))) + (deftype Element [tag attrs content meta] ;; serializing/cloning, hashing, equality, iteration @@ -41,7 +50,7 @@ IHashEq (hasheq [this] (APersistentMap/mapHasheq this)) Iterable - (iterator [this] (RecordIterator. this [:tag :attrs :content] (RT/iter nil)))] + (iterator [this] (ElementIterator. this '(:tag :attrs :content)))] :cljs [ICloneable (-clone [_] (Element. tag attrs content meta)) diff --git a/src/test/clojure/clojure/data/xml/test_equiv.cljc b/src/test/clojure/clojure/data/xml/test_equiv.cljc index cdfdc72..555da6d 100644 --- a/src/test/clojure/clojure/data/xml/test_equiv.cljc +++ b/src/test/clojure/clojure/data/xml/test_equiv.cljc @@ -8,6 +8,4 @@ (element :foo) {:tag :foo :attrs {} :content []} (element (qname "DAV:" "foo")) {:tag (qname "DAV:" "foo") :attrs {} :content []} (element :foo {:a "b"}) {:tag :foo :attrs {:a "b"} :content []} - (element :foo {:a "b"} "a" "b") {:tag :foo :attrs {:a "b"} :content ["a" "b"]} - #?@(:clj ;; wait for https://github.com/clojure/clojurescript/commit/9484a134bdf039c10ec3c26c8aaa3acd0dcd9875 - [#xml/element {:tag :foo :content ["C"]} {:tag :foo :attrs {} :content ["C"]}]))) + (element :foo {:a "b"} "a" "b") {:tag :foo :attrs {:a "b"} :content ["a" "b"]})) From b5e8e3f8bccd8c99ca707b06fa5d6a6d048e88db Mon Sep 17 00:00:00 2001 From: "Hudson @ build.clojure.org" Date: Sat, 24 Dec 2016 22:32:02 -0600 Subject: [PATCH 138/237] [maven-release-plugin] prepare release data.xml-0.2.0-alpha2 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ec074d4..f016820 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ 4.0.0 data.xml - 0.2.0-SNAPSHOT + 0.2.0-alpha2 data.xml jar Functions to parse XML into lazy sequences and lazy trees and emit these as text @@ -120,7 +120,7 @@ scm:git:git@github.com:clojure/data.xml.git scm:git:git@github.com:clojure/data.xml.git git@github.com:clojure/data.xml.git - HEAD + data.xml-0.2.0-alpha2 From 8e8b933ac6c5e7135df2241eae8582ddeeb7aea8 Mon Sep 17 00:00:00 2001 From: "Hudson @ build.clojure.org" Date: Sat, 24 Dec 2016 22:32:02 -0600 Subject: [PATCH 139/237] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index f016820..ec074d4 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ 4.0.0 data.xml - 0.2.0-alpha2 + 0.2.0-SNAPSHOT data.xml jar Functions to parse XML into lazy sequences and lazy trees and emit these as text @@ -120,7 +120,7 @@ scm:git:git@github.com:clojure/data.xml.git scm:git:git@github.com:clojure/data.xml.git git@github.com:clojure/data.xml.git - data.xml-0.2.0-alpha2 + HEAD From 0599423f9f18c09527e2abb56f80ed309e6df3a1 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Sun, 25 Dec 2016 05:38:05 +0100 Subject: [PATCH 140/237] update README for 0.2.0-alpha2 --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ff21048..ec42f91 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ Add the following to the `project.clj` dependencies: ## Installation - Alpha -Latest alpha release: 0.2.0-alpha1 +Latest alpha release: 0.2.0-alpha2 * [All Released Versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22data.xml%22) @@ -51,13 +51,13 @@ For Maven projects, add the following XML in your `pom.xml`'s `` s org.clojure data.xml - 0.2.0-alpha1 + 0.2.0-alpha2 ### Leiningen Add the following to the `project.clj` dependencies: - [org.clojure/data.xml "0.2.0-alpha1"] + [org.clojure/data.xml "0.2.0-alpha2"] ## Examples From d4561f493e0bae2e85eb054017263fc9fddf8bf9 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Tue, 14 Feb 2017 13:48:08 +0100 Subject: [PATCH 141/237] Require clojure 1.7.0 and remove plugins as requested by Alex Miller --- CHANGES.md | 3 +++ pom.xml | 43 +------------------------------------------ 2 files changed, 4 insertions(+), 42 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index f816fb1..a406b55 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,6 @@ +From 0.2.0-alpha2 to 0.2.0-alpha3 +- Minimum requirement is now clojure 1.7.0 + From 0.2.0-alpha1 to 0.2.0-alpha2 - qname function now returns canonical (keyword) names - Remove QName defrecord from Clojurescript diff --git a/pom.xml b/pom.xml index ec074d4..7a5081d 100644 --- a/pom.xml +++ b/pom.xml @@ -59,53 +59,12 @@ ${project.basedir}/src/test/clojurescript - - - - net.bendlas - cljc-maven-plugin - 0.1.2 - - - split-compile - generate-sources - split - - - split-compile-tests - generate-test-sources - split-tests - - - - - maven-jar-plugin - 2.4 - - - default-jar - package - - jar - - - - **/*.clj - **/*.cljs - **/*.cljc - - - - - - - 1.5.0 + 1.7.0 From b0f1e963bf921611a48f28b6c3e2c7241effef1a Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Tue, 14 Feb 2017 13:50:36 +0100 Subject: [PATCH 142/237] Test on cljs 1.9.473; simplify cljs tests --- pom.xml | 2 +- project.clj | 2 +- .../clojure/data/xml/cljs_testsuite.clj | 20 ++++++++----------- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/pom.xml b/pom.xml index 7a5081d..e0f5cb8 100644 --- a/pom.xml +++ b/pom.xml @@ -45,7 +45,7 @@ org.clojure clojurescript - 1.9.293 + 1.9.473 test diff --git a/project.clj b/project.clj index 0c3678d..4aadebd 100644 --- a/project.clj +++ b/project.clj @@ -3,7 +3,7 @@ :test-paths ["src/test/clojure" "src/test/clojurescript"] :resource-paths ["src/test/resources" "target/gen-resources"] :dependencies [[org.clojure/clojure "1.8.0"] - [org.clojure/clojurescript "1.9.293"] + [org.clojure/clojurescript "1.9.473"] [com.cemerick/piggieback "0.2.1"] [org.clojure/tools.nrepl "0.2.12"] [org.clojure/test.check "0.9.0"] diff --git a/src/test/resources/clojure/data/xml/cljs_testsuite.clj b/src/test/resources/clojure/data/xml/cljs_testsuite.clj index 5e03240..7820d1d 100644 --- a/src/test/resources/clojure/data/xml/cljs_testsuite.clj +++ b/src/test/resources/clojure/data/xml/cljs_testsuite.clj @@ -16,28 +16,24 @@ "cljs-nashorn-" (into-array FileAttribute [])))) (defn compile-testsuite! [dir] - (let [out (io/file dir "tests.js")] - (println "Building in" dir) - (bapi/build (bapi/inputs "src/main/clojure" "src/test/clojure" "src/test/clojurescript") + (let [out (io/file dir "tests.js") + inputs ["src/main/clojure" "src/test/clojure" "src/test/clojurescript"]] + (println "INFO" "Compiling cljs testsuite from" inputs "into" (str out)) + (bapi/build (apply bapi/inputs inputs) {:output-to (str out) :output-dir dir :main 'clojure.data.xml.test-cljs :optimizations :advanced :pseudo-names true - :preamble ["dxml-nashorn.generated.js"]}) - (spit (io/file dir "tests.reopt.js") - (closure/optimize {:optimizations :simple - :pretty-print true - :closure-warnings - {:non-standard-jsdoc :off}} - (slurp out))))) + :pretty-print true + :preamble ["dxml-nashorn.generated.js"]}))) (defn run-testsuite! [dir] (System/setProperty "nashorn.persistent.code.cache" "target/nashorn_code_cache") (let [engine (repl-nh/create-engine)] - (println "INFO" "Running nashorn-repl with" (System/getProperty "nashorn.persistent.code.cache")) (compile-testsuite! dir) - (.eval engine (io/reader (io/file dir "tests.reopt.js"))) + (println "INFO" "Running cljs tests in nashorn with persistent code cache in" (System/getProperty "nashorn.persistent.code.cache")) + (.eval engine (io/reader (io/file dir "tests.js"))) (let [{:as res :keys [fail error]} (read-string (.eval engine "clojure.data.xml.test_cljs._main_nashorn()"))] (is (and (zero? fail) (zero? error)) (pr-str res))))) From 2a202da0e99a98a8a5bba8a8129e948412ad58a7 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Tue, 14 Feb 2017 14:11:39 +0100 Subject: [PATCH 143/237] DXML-35 print newline after preamble --- CHANGES.md | 1 + src/main/clojure/clojure/data/xml/jvm/pprint.clj | 4 +++- src/test/clojure/clojure/data/xml/test_pprint.clj | 6 +----- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index a406b55..c768338 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,6 @@ From 0.2.0-alpha2 to 0.2.0-alpha3 - Minimum requirement is now clojure 1.7.0 +- Print newline after preamble when pretty-printing (DXML-35) From 0.2.0-alpha1 to 0.2.0-alpha2 - qname function now returns canonical (keyword) names diff --git a/src/main/clojure/clojure/data/xml/jvm/pprint.clj b/src/main/clojure/clojure/data/xml/jvm/pprint.clj index 4d04210..1c26a39 100644 --- a/src/main/clojure/clojure/data/xml/jvm/pprint.clj +++ b/src/main/clojure/clojure/data/xml/jvm/pprint.clj @@ -16,7 +16,9 @@ (doto (-> (TransformerFactory/newInstance) .newTransformer) (.setOutputProperty (OutputKeys/INDENT) "yes") (.setOutputProperty (OutputKeys/METHOD) "xml") - (.setOutputProperty "{http://xml.apache.org/xslt}indent-amount" "2"))) + (.setOutputProperty "{http://xml.apache.org/xslt}indent-amount" "2") + ;; print newline after preamble https://bugs.openjdk.java.net/browse/JDK-7150637 + (.setOutputProperty "http://www.oracle.com/xml/is-standalone" "yes"))) (defn indent-xml [xml-str ^Writer writer] diff --git a/src/test/clojure/clojure/data/xml/test_pprint.clj b/src/test/clojure/clojure/data/xml/test_pprint.clj index b70a35d..d9693ca 100644 --- a/src/test/clojure/clojure/data/xml/test_pprint.clj +++ b/src/test/clojure/clojure/data/xml/test_pprint.clj @@ -16,14 +16,10 @@ (def xml "") -(defn jdk8? [] - (-> (System/getProperty "java.version") - (.startsWith "1.8"))) - (def indented-xml (str "" - (when-not (jdk8?) "\n") + "\n" " From 4f9da0af3178df7c69395514329615c2e1b4314f Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Tue, 14 Feb 2017 16:59:37 +0100 Subject: [PATCH 144/237] remove tag from EndElementEvent this data was unused and it was set wrong --- src/main/clojure/clojure/data/xml/event.clj | 11 +++++++---- src/main/clojure/clojure/data/xml/jvm/parse.clj | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main/clojure/clojure/data/xml/event.clj b/src/main/clojure/clojure/data/xml/event.clj index fdae476..5109ab1 100644 --- a/src/main/clojure/clojure/data/xml/event.clj +++ b/src/main/clojure/clojure/data/xml/event.clj @@ -26,13 +26,16 @@ attrs #(merge-nss (element-nss* element) %2))) ; Represents a parse event. -; type is one of :start-element, :end-element, or :characters (defrecord StartElementEvent [tag attrs nss location-info]) -(defrecord EndElementEvent [tag]) (defrecord CharsEvent [str]) (defrecord CDataEvent [str]) (defrecord CommentEvent [str]) +;; EndElementEvent doesn't have any data, so make it a singleton +(deftype EndElementEvent []) +(def end-element-event (EndElementEvent.)) +(defn ->EndElementEvent [] end-element-event) + ;; Event Generation for stuff to show up in generated xml (let [second-arg #(do %2) @@ -42,7 +45,7 @@ attrs #(->StartElementEvent tag %1 (merge-nss (element-nss* element) %2) nil))) :next-events (fn elem-next-events [{:keys [tag content]} next-items] - (list* content (->EndElementEvent tag) next-items))}] + (list* content end-element-event next-items))}] (extend-protocol-fns EventGeneration (StartElementEvent EndElementEvent CharsEvent CDataEvent CommentEvent) @@ -87,4 +90,4 @@ :else (throw (ex-info "Illegal argument, not an event object" {:event event})))) (defn event-exit? [event] - (instance? EndElementEvent event)) + (identical? end-element-event event)) diff --git a/src/main/clojure/clojure/data/xml/jvm/parse.clj b/src/main/clojure/clojure/data/xml/jvm/parse.clj index dba8035..0b9c645 100644 --- a/src/main/clojure/clojure/data/xml/jvm/parse.clj +++ b/src/main/clojure/clojure/data/xml/jvm/parse.clj @@ -88,7 +88,7 @@ XMLStreamConstants/END_ELEMENT (if (include-node? :element) (do (assert (seq ns-envs) "Balanced end") - (cons (->EndElementEvent (keyword (.getLocalName sreader))) + (cons (->EndElementEvent) (pull-seq sreader opts (rest ns-envs)))) (recur)) XMLStreamConstants/CHARACTERS From 97b6d8c26e324ee339154ae1eeebf243b8ba1202 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Tue, 14 Feb 2017 17:05:23 +0100 Subject: [PATCH 145/237] DXML-39 fix error message on encoding mismatch --- src/main/clojure/clojure/data/xml/jvm/emit.clj | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/clojure/data/xml/jvm/emit.clj b/src/main/clojure/clojure/data/xml/jvm/emit.clj index 2d0dfed..119a19f 100644 --- a/src/main/clojure/clojure/data/xml/jvm/emit.clj +++ b/src/main/clojure/clojure/data/xml/jvm/emit.clj @@ -29,9 +29,11 @@ (defn check-stream-encoding [^OutputStreamWriter stream xml-encoding] (when (not= (Charset/forName xml-encoding) (Charset/forName (.getEncoding stream))) - (throw (Exception. (str "Output encoding of stream (" xml-encoding - ") doesn't match declaration (" - (.getEncoding stream) ")"))))) + (throw (ex-info (str "Output encoding of writer (" (.getEncoding stream) + ") doesn't match declaration (" + xml-encoding ")") + {:stream-encoding (.getEncoding stream) + :declared-encoding xml-encoding})))) ;; properly namespace aware version (defn- emit-attrs [^XMLStreamWriter writer attrs] From 82195ad23db333bdc55b1fccf5495a7de3c3a99c Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Tue, 14 Feb 2017 20:59:45 +0100 Subject: [PATCH 146/237] Implement bidirectional map for prefix-xmlns mapping --- src/main/clojure/clojure/data/xml/name.cljc | 4 +- src/main/clojure/clojure/data/xml/pu_map.cljc | 74 +++++++++++++++++++ .../clojure/clojure/data/xml/test_pu.cljc | 60 +++++++++++++++ .../clojure/data/xml/test_cljs.cljs | 9 ++- 4 files changed, 142 insertions(+), 5 deletions(-) create mode 100644 src/main/clojure/clojure/data/xml/pu_map.cljc create mode 100644 src/test/clojure/clojure/data/xml/test_pu.cljc diff --git a/src/main/clojure/clojure/data/xml/name.cljc b/src/main/clojure/clojure/data/xml/name.cljc index dcac3f3..8200769 100644 --- a/src/main/clojure/clojure/data/xml/name.cljc +++ b/src/main/clojure/clojure/data/xml/name.cljc @@ -62,10 +62,10 @@ (keyword? ns) (name ns) :else (str ns))) -;; xmlns attributes get special treatment, as they are go into metadata and don't contribute to equality +;; xmlns attributes get special treatment. they go into metadata, don't contribute to equality (def xmlns-uri "http://www.w3.org/2000/xmlns/") ;; TODO find out if xml prefixed names need any special treatment too - ; (def xml-uri "http://www.w3.org/XML/1998/namespace") +(def xml-uri "http://www.w3.org/XML/1998/namespace") (extend-protocol AsQName Keyword diff --git a/src/main/clojure/clojure/data/xml/pu_map.cljc b/src/main/clojure/clojure/data/xml/pu_map.cljc new file mode 100644 index 0000000..6cbfb61 --- /dev/null +++ b/src/main/clojure/clojure/data/xml/pu_map.cljc @@ -0,0 +1,74 @@ +(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])) + +(defn transient [{:keys [u->ps p->u]}] + (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] + (when (or (core/get #{"xml" "xmlns"} prefix) + (core/get #{name/xml-uri name/xmlns-uri} uri)) + (throw (ex-info "Mapping for xml: and xmlns: prefixes are fixed by the standard" + {:attempted-mapping {:prefix prefix + :uri uri}}))) + (let [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 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))) + + +;; 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}}) diff --git a/src/test/clojure/clojure/data/xml/test_pu.cljc b/src/test/clojure/clojure/data/xml/test_pu.cljc new file mode 100644 index 0000000..73c495e --- /dev/null +++ b/src/test/clojure/clojure/data/xml/test_pu.cljc @@ -0,0 +1,60 @@ +(ns clojure.data.xml.test-pu + (:require [clojure.data.xml.pu-map :as pu] + [clojure.data.xml.name :as name] + [clojure.test :refer [deftest is are testing]])) + +(deftest builtin-mappings + (is (= name/xml-uri (pu/get pu/EMPTY "xml"))) + (is (= name/xmlns-uri (pu/get pu/EMPTY "xmlns"))) + (is (= ["xml"] (pu/get-prefixes pu/EMPTY name/xml-uri))) + (is (= ["xmlns"] (pu/get-prefixes pu/EMPTY name/xmlns-uri))) + (are [p u] (thrown? #?(:clj Exception :cljs js/Error) (pu/assoc pu/EMPTY p u)) + "xml" "_" + "xmlns" "_" + "_" name/xml-uri + "_" name/xmlns-uri)) + +(deftest basic-operation + (are [associated-groups expected-uris expected-prefixes] + (let [pu (reduce (fn [pu* group] (apply pu/assoc pu* group)) + pu/EMPTY associated-groups)] + (every? true? + (apply concat + (for [[prefix uri] (partition 2 expected-uris)] + (is (= uri (pu/get pu prefix)))) + (for [[uri prefixes] (partition 2 expected-prefixes)] + [(is (= prefixes (pu/get-prefixes pu uri))) + (is (= (first prefixes) (pu/get-prefix pu uri)))])))) + [] + ["wrong-prefix" nil + "xml" name/xml-uri + "xmlns" name/xmlns-uri] + ["wrong-uri" nil + name/xml-uri ["xml"] + name/xmlns-uri ["xmlns"]] + + [["p" "U:" + "q" "V:"]] + ["wrong-prefix" nil + "xml" name/xml-uri + "xmlns" name/xmlns-uri + "p" "U:" + "q" "V:"] + ["wrong-uri" nil + name/xml-uri ["xml"] + name/xmlns-uri ["xmlns"] + "U:" ["p"] + "V:" ["q"]] + + [["p" "U:" + "q" "V:"] + ["r" "U:" + "s" "V:"] + ["t" "U:"] + ["p" "" + "q" ""]] + ["p" nil + "q" nil + "r" "U:"] + ["U:" ["r" "t"] + "V:" ["s"]])) diff --git a/src/test/clojurescript/clojure/data/xml/test_cljs.cljs b/src/test/clojurescript/clojure/data/xml/test_cljs.cljs index dc956ac..e05d837 100644 --- a/src/test/clojurescript/clojure/data/xml/test_cljs.cljs +++ b/src/test/clojurescript/clojure/data/xml/test_cljs.cljs @@ -3,7 +3,8 @@ [clojure.data.xml :as xml] clojure.data.xml.test-cljs-basic clojure.data.xml.test-cljs-extended - clojure.data.xml.test-equiv)) + clojure.data.xml.test-equiv + clojure.data.xml.test-pu)) (def ^:dynamic *results*) @@ -19,13 +20,15 @@ (binding [*results* nil] (println "Running Basic Tests") (test/run-tests 'clojure.data.xml.test-cljs-basic - 'clojure.data.xml.test-equiv) + 'clojure.data.xml.test-equiv + 'clojure.data.xml.test-pu) (pr-str *results*))) (defn ^:export -main [] (binding [*results* nil] (println "Running Basic Tests") - (test/run-tests 'clojure.data.xml.test-cljs-basic) + (test/run-tests 'clojure.data.xml.test-cljs-basic + 'clojure.data.xml.test-pu) (println "Extending DOM Objects and running again + extended tests") (xml/extend-dom-as-data!) (test/testing "with extended native dom" From 75e5669637381cf5482e338a0a04de484b3d3b42 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Tue, 14 Feb 2017 22:47:39 +0100 Subject: [PATCH 147/237] Don't rely on the namespace context provided by the emitter The StAX version built-in to JDK has a bug in its namespace context implementation, where declared namespaces would bleed over to sibling, for self-closing tags. This makes it unusable for DXML-25 We use our own bidirectional map impl for keeping track of the namespace environment. This also simplifies the code somewhat, since we don't have to work around interactions between the mutable context and the emitter any more. --- .../clojure/clojure/data/xml/jvm/emit.clj | 155 +++++++++--------- src/main/clojure/clojure/data/xml/pu_map.cljc | 15 ++ .../clojure/clojure/data/xml/test_pu.cljc | 8 + 3 files changed, 101 insertions(+), 77 deletions(-) diff --git a/src/main/clojure/clojure/data/xml/jvm/emit.clj b/src/main/clojure/clojure/data/xml/jvm/emit.clj index 119a19f..0978e3f 100644 --- a/src/main/clojure/clojure/data/xml/jvm/emit.clj +++ b/src/main/clojure/clojure/data/xml/jvm/emit.clj @@ -11,6 +11,7 @@ {:author "Herwig Hochleitner"} (:require (clojure.data.xml [name :refer [qname-uri qname-local separate-xmlns gen-prefix *gen-prefix-counter*]] + [pu-map :as pu] event) [clojure.string :as str]) (:import (java.io OutputStreamWriter Writer StringWriter) @@ -25,7 +26,7 @@ (def logger (Logger/getLogger "clojure.data.xml")) (defprotocol EventEmit - (emit-event [event ^XMLStreamWriter writer])) + (emit-event [event ^XMLStreamWriter writer prefix-uri-stack])) (defn check-stream-encoding [^OutputStreamWriter stream xml-encoding] (when (not= (Charset/forName xml-encoding) (Charset/forName (.getEncoding stream))) @@ -35,79 +36,76 @@ {:stream-encoding (.getEncoding stream) :declared-encoding xml-encoding})))) -;; properly namespace aware version -(defn- emit-attrs [^XMLStreamWriter writer attrs] - (doseq [[k v] attrs] - (let [uri (qname-uri k) - local (qname-local k)] - (if (str/blank? uri) - (.writeAttribute writer local (str v)) - (.writeAttribute writer uri local (str v)))))) - -(defn- make-prefix [^NamespaceContext nc] - (let [pf (gen-prefix)] - (if (str/blank? (.getNamespaceURI nc pf)) - pf (recur nc)))) - -(defn- write-xmlns-attribute [^XMLStreamWriter writer k v] - (if (str/blank? k) - (do (.setDefaultNamespace writer v) - (.writeDefaultNamespace writer v)) - (do (.setPrefix writer v k) - (.writeNamespace writer v k))) - writer) - -(defn- get-prefix [^XMLStreamWriter writer temp-xmlns uri] - (or (get temp-xmlns uri) - (.getPrefix writer uri))) - -(defn- xmlns-attribute-set [^XMLStreamWriter writer ns-attrs used-uris] - (let [tleft (transient {}) - tleft (reduce-kv (fn [tleft k v] - (let [local (qname-local k)] - (or (if (= "xmlns" local) - (when-not (= (str v) - (str (.. writer getNamespaceContext (getNamespaceURI "")))) - (assoc! tleft v "")) - (when-let [prefix (and (str/blank? (get-prefix writer tleft v)) - (if (.. writer getNamespaceContext - (getNamespaceURI local)) - ;; rename clashing prefixes - (make-prefix (.getNamespaceContext writer)) - local))] - (assoc! tleft v prefix))) - tleft))) - tleft ns-attrs)] - (persistent! - (reduce (fn [tleft uri] - (if (and (not (str/blank? uri)) - (nil? (get-prefix writer tleft uri))) - (assoc! tleft uri (make-prefix (.getNamespaceContext writer))) - tleft)) - tleft used-uris)))) - -(defn- emit-start-tag [{:keys [attrs nss tag]} ^XMLStreamWriter writer] +(defn- emit-attrs [^XMLStreamWriter writer pu attrs] + (reduce-kv + (fn [_ attr value] + (let [uri (qname-uri attr) + local (qname-local attr)] + (if (str/blank? uri) + (.writeAttribute writer local value) + (.writeAttribute writer (pu/get-prefix pu uri) uri local value))) + _) + nil attrs)) + +(defn- emit-ns-attrs [^XMLStreamWriter writer parent-pu pu] + (pu/reduce-diff + (fn [_ pf uri] + (if (str/blank? pf) + (.writeDefaultNamespace writer uri) + (.writeNamespace writer pf uri)) + _) + nil parent-pu pu)) + +(defn- compute-prefix [tpu uri suggested] + (or (pu/get-prefix tpu uri) + (loop [prefix (or suggested (gen-prefix))] + (if (pu/get tpu prefix) + (recur (gen-prefix)) + prefix)))) + +(defn- compute-pu [pu ns-attrs attr-uris tag-uri tag-local] + (let [tpu (pu/transient pu) + ;; add namespaces from current environment + tpu (reduce-kv (fn [tpu ns-attr uri] + (pu/assoc! tpu + (if (str/blank? (qname-uri ns-attr)) + (do (assert (= "xmlns" (qname-local ns-attr)) + "non-prefixed attribute, that's not xmlns= is not a namespace attr") + "") + (compute-prefix tpu uri (qname-local ns-attr))) + uri)) + tpu ns-attrs) + ;; add implicit namespaces used by tag, attrs + tpu (reduce (fn [tpu uri] + (pu/assoc! tpu (compute-prefix tpu uri nil) uri)) + tpu (if (str/blank? tag-uri) + attr-uris + (cons tag-uri attr-uris))) + ;; rename default namespace, if tag is global (not in a namespace) + tpu (if-let [uri (and (str/blank? tag-uri) + (pu/get tpu ""))] + (do + (when (.isLoggable logger Level/FINE) + (.log logger Level/FINE + (format "Default `xmlns=\"%s\"` had to be replaced with a `xmlns=\"\"` because of global element `%s`" + uri tag-local))) + (-> tpu + (pu/assoc! "" "") + (as-> tpu (pu/assoc! tpu (compute-prefix tpu uri nil) uri)))) + tpu)] + (pu/persistent! tpu))) + +(defn- emit-start-tag [{:keys [attrs nss tag]} ^XMLStreamWriter writer prefix-uri-stack] (let [uri (qname-uri tag) local (qname-local tag) - global (str/blank? uri) - xmlns-attrs (xmlns-attribute-set - writer - (if global - (let [default (get nss :xmlns)] - (when (and - (not (str/blank? default)) - (.isLoggable logger Level/FINE)) - (.log logger Level/FINE - (format "Default `xmlns=\"%s\"` had to be replaced with a `xmlns=\"\"` because of global element `%s`" default local))) - (assoc nss :xmlns "")) - nss) - (cons uri (map qname-uri (keys attrs))))] - (if global + parent-pu (first prefix-uri-stack) + pu (compute-pu parent-pu nss (map qname-uri (keys attrs)) uri local)] + (if (str/blank? uri) (.writeStartElement writer local) - (.writeStartElement writer (get-prefix writer xmlns-attrs uri) - local uri)) - (reduce-kv write-xmlns-attribute writer xmlns-attrs) - (emit-attrs writer attrs))) + (.writeStartElement writer (pu/get-prefix pu uri) local uri)) + (emit-ns-attrs writer parent-pu pu) + (emit-attrs writer pu attrs) + (cons pu prefix-uri-stack))) (defn- emit-cdata [^String cdata-str ^XMLStreamWriter writer] (when-not (str/blank? cdata-str) @@ -120,15 +118,18 @@ (extend-protocol EventEmit StartElementEvent - (emit-event [ev writer] (emit-start-tag ev writer)) + (emit-event [ev writer pu-stack] (emit-start-tag ev writer pu-stack)) EndElementEvent - (emit-event [ev writer] (.writeEndElement writer)) + (emit-event [ev writer pu-stack] + (assert (next pu-stack) "balanced tags") + (.writeEndElement writer) + (next pu-stack)) CharsEvent - (emit-event [{:keys [str]} writer] (.writeCharacters writer str)) + (emit-event [{:keys [str]} writer s] (.writeCharacters writer str) s) CDataEvent - (emit-event [{:keys [str]} writer] (emit-cdata str writer)) + (emit-event [{:keys [str]} writer s] (emit-cdata str writer) s) CommentEvent - (emit-event [{:keys [str]} writer] (.writeComment writer str))) + (emit-event [{:keys [str]} writer s] (.writeComment writer str) s)) ;; Writers @@ -148,7 +149,7 @@ (.writeStartDocument writer (or (:encoding opts) "UTF-8") "1.0") (when-let [doctype (:doctype opts)] (.writeDTD writer doctype)) - (doseq [event events] (emit-event event writer)) + (reduce #(emit-event %2 writer %1) [pu/EMPTY] events) (.writeEndDocument writer) swriter))) diff --git a/src/main/clojure/clojure/data/xml/pu_map.cljc b/src/main/clojure/clojure/data/xml/pu_map.cljc index 6cbfb61..d6c21fe 100644 --- a/src/main/clojure/clojure/data/xml/pu_map.cljc +++ b/src/main/clojure/clojure/data/xml/pu_map.cljc @@ -72,3 +72,18 @@ name/xmlns-uri ["xmlns"]} :p->u {"xml" name/xml-uri "xmlns" name/xmlns-uri}}) + +(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)) diff --git a/src/test/clojure/clojure/data/xml/test_pu.cljc b/src/test/clojure/clojure/data/xml/test_pu.cljc index 73c495e..fe40989 100644 --- a/src/test/clojure/clojure/data/xml/test_pu.cljc +++ b/src/test/clojure/clojure/data/xml/test_pu.cljc @@ -58,3 +58,11 @@ "r" "U:"] ["U:" ["r" "t"] "V:" ["s"]])) + +(deftest diffing + (is (= {"c" "d"} + (pu/reduce-diff + assoc {} + (pu/assoc pu/EMPTY "a" "b") + (pu/assoc pu/EMPTY + "a" "b" "c" "d"))))) From 8dab1ab091b4024ca2e140427b298593afb752d4 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Tue, 14 Feb 2017 22:57:35 +0100 Subject: [PATCH 148/237] remove vestigeal code from tests --- src/test/clojure/clojure/data/xml/test_emit.clj | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/test/clojure/clojure/data/xml/test_emit.clj b/src/test/clojure/clojure/data/xml/test_emit.clj index 396f842..403dec1 100644 --- a/src/test/clojure/clojure/data/xml/test_emit.clj +++ b/src/test/clojure/clojure/data/xml/test_emit.clj @@ -56,9 +56,6 @@ {:single "'single'quotes'here" :double "\"double\"quotes\"here\""})))))) -;; TODO add an indentation test once we figure out how to indent portably across JREs - - (defn emit-char-seq [xml-tree encoding] (with-open [bos (java.io.ByteArrayOutputStream.) stream (java.io.OutputStreamWriter. bos encoding)] @@ -134,11 +131,6 @@ (xml-comment " goes here ") " not here"))))) -(def xml-decl-newline? - (-> (System/getProperty "java.version") - (.startsWith "1.8") - not)) - (deftest test-indent (let [nested-xml (lazy-parse* (str "foo")) expect (str "\n \n \n foo\n \n \n\n") From eaf3862feac31c85295bef9e71a44e6ffec091ea Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Tue, 14 Feb 2017 23:33:57 +0100 Subject: [PATCH 149/237] DXML-27 test serialization of built-in data types as per https://www.w3.org/TR/xmlschema-2/#built-in-datatypes --- .../clojure/clojure/data/xml/test_emit.clj | 64 +++++++++++++++---- 1 file changed, 50 insertions(+), 14 deletions(-) diff --git a/src/test/clojure/clojure/data/xml/test_emit.clj b/src/test/clojure/clojure/data/xml/test_emit.clj index 403dec1..385ed1d 100644 --- a/src/test/clojure/clojure/data/xml/test_emit.clj +++ b/src/test/clojure/clojure/data/xml/test_emit.clj @@ -12,7 +12,8 @@ (:require [clojure.test :refer :all] [clojure.data.xml :refer :all] - [clojure.data.xml.test-utils :refer [test-stream lazy-parse*]])) + [clojure.data.xml.test-utils :refer [test-stream lazy-parse*]]) + (:import (javax.xml.namespace QName))) (def deep-tree (lazy-parse* (str "" @@ -153,19 +154,54 @@ result (indent-str nested-xml :doctype doctype)] (is (= expect (subs result (.indexOf result doctype)))))) -(deftest test-boolean - (is (= "true" - (emit-str (element :foo {} true))))) - -(deftest test-number - (is (= "1" - (emit-str (element :foo {} 1)))) - (is (= "1.2" - (emit-str (element :foo {} 1.2)))) - (is (= "0" - (emit-str (element :foo {} (int 0))))) - (is (= "1.2" - (emit-str (element :foo {} (float 1.2)))))) +(defmacro are-serializable [group-description extra-attrs & {:as data-strings}] + `(testing ~group-description + (testing "in content" + ~@(for [[data string] data-strings] + `(is (= (parse-str (emit-str (element :e ~extra-attrs ~string))) + (parse-str (emit-str (element :e ~extra-attrs ~data))))))) + (testing "in attrs" + ~@(for [[data string] data-strings] + `(is (= (emit-str (element :e ~(assoc extra-attrs :a string))) + (emit-str (element :e ~(assoc extra-attrs :a data))))))))) + +(deftest test-datatypes + ;; https://www.w3.org/TR/xmlschema-2/#built-in-datatypes + (testing "serializing" + (are-serializable + "booleans" {} + true "true" + false "false") + (are-serializable + "numbers" {} + 1 "1" + 1.2 "1.2" + 3/4 "0.75" + (int 0) "0" + (float 1.4) "1.4" + 1.25M "1.25" + (BigInteger. "42424242424242424242424242424242") "42424242424242424242424242424242" + 42424242424242424242424242424242 "42424242424242424242424242424242") + (are-serializable + "byte-arrays" {} + (byte-array [0 1 2 3 4]) "AAECAwQ=") + (are-serializable + "uris" {} + (java.net.URI. "S:l") "S:l" + (java.net.URL. "http://foo") "http://foo") + (are-serializable + "dates" {} + (java.util.Date. 0) "1970-01-01T00:00:00.000-00:00" + (java.time.Instant/ofEpochMilli 0) "1970-01-01T00:00:00.000-00:00") + (are-serializable + "qnames" {:xmlns/p "U:"} + :xmlns.U%3A/qn "p:qn" + (QName. "U:" "qn") "p:qn") + (testing "qnames generated" + (is (thrown? Exception (emit-str (element :e {} :xmlns.U%3A/qn)))) + (is (thrown? Exception (emit-str (element :e {:a :xmlns.U%3A/qn})))) + (is (thrown? Exception (emit-str (element :e {} (QName. "U:" "qn"))))) + (is (thrown? Exception (emit-str (element :e {:a (QName. "U:" "qn")}))))))) (deftest test-event-seq-emit (is (= "123" From e7d7deebb59be833aa0a8211c8aea61b8810ebb7 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Thu, 16 Feb 2017 16:46:52 +0100 Subject: [PATCH 150/237] DXML-27 serialize XMLSchema built-in data types serialize well-known java/clojure data types with a correspondence in https://www.w3.org/TR/xmlschema-2/#built-in-datatypes --- pom.xml | 6 ++ project.clj | 1 + src/main/clojure/clojure/data/xml/event.clj | 34 +++++++--- src/main/clojure/clojure/data/xml/impl.clj | 17 ++++- .../clojure/clojure/data/xml/jvm/emit.clj | 64 +++++++++++++++++-- .../clojure/clojure/data/xml/protocols.cljc | 3 + 6 files changed, 109 insertions(+), 16 deletions(-) diff --git a/pom.xml b/pom.xml index e0f5cb8..cb860c7 100644 --- a/pom.xml +++ b/pom.xml @@ -36,6 +36,12 @@ + + org.clojure + data.codec + 0.1.0 + compile + org.clojure test.check diff --git a/project.clj b/project.clj index 4aadebd..a2b5e1b 100644 --- a/project.clj +++ b/project.clj @@ -3,6 +3,7 @@ :test-paths ["src/test/clojure" "src/test/clojurescript"] :resource-paths ["src/test/resources" "target/gen-resources"] :dependencies [[org.clojure/clojure "1.8.0"] + [org.clojure/data.codec "0.1.0"] [org.clojure/clojurescript "1.9.473"] [com.cemerick/piggieback "0.2.1"] [org.clojure/tools.nrepl "0.2.12"] diff --git a/src/main/clojure/clojure/data/xml/event.clj b/src/main/clojure/clojure/data/xml/event.clj index 5109ab1..aa7384f 100644 --- a/src/main/clojure/clojure/data/xml/event.clj +++ b/src/main/clojure/clojure/data/xml/event.clj @@ -10,11 +10,15 @@ "Data type for xml pull events" {:author "Herwig Hochleitner"} (:require [clojure.data.xml.protocols :refer - [EventGeneration gen-event next-events]] + [EventGeneration gen-event next-events xml-str]] [clojure.data.xml.name :refer [merge-nss separate-xmlns]] [clojure.data.xml.node :refer [element* cdata xml-comment]] - [clojure.data.xml.impl :refer [extend-protocol-fns]]) - (:import (clojure.data.xml.node Element CData Comment))) + [clojure.data.xml.impl :refer [extend-protocol-fns compile-if]]) + (:import (clojure.data.xml.node Element CData Comment) + (clojure.lang Sequential IPersistentMap Keyword) + (java.net URI URL) + (java.util Date) + (javax.xml.namespace QName))) (definline element-nss* [element] `(get (meta ~element) :clojure.data.xml/nss {})) @@ -30,6 +34,7 @@ (defrecord CharsEvent [str]) (defrecord CDataEvent [str]) (defrecord CommentEvent [str]) +(defrecord QNameEvent [qn]) ;; EndElementEvent doesn't have any data, so make it a singleton (deftype EndElementEvent []) @@ -45,26 +50,35 @@ attrs #(->StartElementEvent tag %1 (merge-nss (element-nss* element) %2) nil))) :next-events (fn elem-next-events [{:keys [tag content]} next-items] - (list* content end-element-event next-items))}] + (list* content end-element-event next-items))} + string-event-generation {:gen-event (comp ->CharsEvent #'xml-str) + :next-events second-arg} + qname-event-generation {:gen-event ->QNameEvent + :next-events second-arg}] (extend-protocol-fns EventGeneration (StartElementEvent EndElementEvent CharsEvent CDataEvent CommentEvent) {:gen-event identity :next-events second-arg} - (String Boolean Number nil) - {:gen-event (comp ->CharsEvent str) - :next-events second-arg} + (String Boolean Number (Class/forName "[B") Date URI URL nil) + string-event-generation + (Keyword QName) qname-event-generation CData {:gen-event (comp ->CDataEvent :content) :next-events second-arg} Comment {:gen-event (comp ->CommentEvent :content) :next-events second-arg} - (clojure.lang.IPersistentMap Element) elem-event-generation)) + (IPersistentMap Element) elem-event-generation) + (compile-if + (Class/forName "java.time.Instant") + (extend java.time.Instant + EventGeneration + string-event-generation) + nil)) (extend-protocol EventGeneration - - clojure.lang.Sequential + Sequential (gen-event [coll] (gen-event (first coll))) (next-events [coll next-items] diff --git a/src/main/clojure/clojure/data/xml/impl.clj b/src/main/clojure/clojure/data/xml/impl.clj index dbbe8cd..77ee759 100644 --- a/src/main/clojure/clojure/data/xml/impl.clj +++ b/src/main/clojure/clojure/data/xml/impl.clj @@ -8,7 +8,8 @@ (ns clojure.data.xml.impl "Shared private code for data.xml namespaces" - {:author "Herwig Hochleitner"}) + {:author "Herwig Hochleitner"} + (:require [clojure.data.codec.base64 :as b64])) (defn- var-form? [form] (and (seq? form) (= 'var (first form)))) @@ -49,3 +50,17 @@ `(let [~mm ~mmap] ~@(map gen-extend type (repeat mm)))) (gen-extend type mmap)))))) + +(defmacro compile-if + "Evaluate `exp` and if it returns logical true and doesn't error, expand to + `then`. Else expand to `else`. + + see clojure.core.reducers" + [exp then else] + (if (try (eval exp) + (catch Throwable _ false)) + `(do ~then) + `(do ~else))) + +(defn b64-encode [ba] + (String. (b64/encode ba))) diff --git a/src/main/clojure/clojure/data/xml/jvm/emit.clj b/src/main/clojure/clojure/data/xml/jvm/emit.clj index 0978e3f..0a1225e 100644 --- a/src/main/clojure/clojure/data/xml/jvm/emit.clj +++ b/src/main/clojure/clojure/data/xml/jvm/emit.clj @@ -12,16 +12,22 @@ (:require (clojure.data.xml [name :refer [qname-uri qname-local separate-xmlns gen-prefix *gen-prefix-counter*]] [pu-map :as pu] + [protocols :refer [AsXmlString xml-str]] + [impl :refer [extend-protocol-fns b64-encode compile-if]] event) [clojure.string :as str]) (:import (java.io OutputStreamWriter Writer StringWriter) (java.nio.charset Charset) (java.util.logging Logger Level) - (javax.xml.namespace NamespaceContext) + (javax.xml.namespace NamespaceContext QName) (javax.xml.stream XMLStreamWriter XMLOutputFactory) (javax.xml.transform OutputKeys Transformer TransformerFactory) - (clojure.data.xml.event StartElementEvent EndElementEvent CharsEvent CDataEvent CommentEvent))) + (clojure.data.xml.event StartElementEvent EndElementEvent CharsEvent CDataEvent CommentEvent QNameEvent) + (clojure.lang BigInt) + (java.net URI URL) + (java.util Date) + (java.text DateFormat SimpleDateFormat))) (def logger (Logger/getLogger "clojure.data.xml")) @@ -36,14 +42,25 @@ {:stream-encoding (.getEncoding stream) :declared-encoding xml-encoding})))) +(defn- prefix-for [qn pu] + (or (pu/get-prefix pu (qname-uri qn)) + (throw (ex-info "Auto-generating prefixes is not supported for content-qnames. Please declare all URIs used in content qnames." + {:qname qn + :uri (qname-uri qn)})))) + +(defn- attr-str [value pu] + (if (or (keyword? value) (instance? QName value)) + (str (prefix-for value pu) ":" (qname-local value)) + (xml-str value))) + (defn- emit-attrs [^XMLStreamWriter writer pu attrs] (reduce-kv (fn [_ attr value] (let [uri (qname-uri attr) local (qname-local attr)] (if (str/blank? uri) - (.writeAttribute writer local value) - (.writeAttribute writer (pu/get-prefix pu uri) uri local value))) + (.writeAttribute writer local (attr-str value pu)) + (.writeAttribute writer (pu/get-prefix pu uri) uri local (attr-str value pu)))) _) nil attrs)) @@ -129,7 +146,44 @@ CDataEvent (emit-event [{:keys [str]} writer s] (emit-cdata str writer) s) CommentEvent - (emit-event [{:keys [str]} writer s] (.writeComment writer str) s)) + (emit-event [{:keys [str]} writer s] (.writeComment writer str) s) + QNameEvent + (emit-event [{:keys [qn]} writer pu-stack] + (.writeCharacters writer (prefix-for qn (first pu-stack))) + (.writeCharacters writer ":") + (.writeCharacters writer (qname-local qn)) + pu-stack)) + +(def ^:private ^ThreadLocal thread-local-utc-date-format + ;; SimpleDateFormat is not thread-safe, so we use a ThreadLocal proxy for access. + ;; http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4228335 + (proxy [ThreadLocal] [] + (initialValue [] + (doto (SimpleDateFormat. "yyyy-MM-dd'T'HH:mm:ss.SSS-00:00") + ;; RFC3339 says to use -00:00 when the timezone is unknown (+00:00 implies a known GMT) + (.setTimeZone (java.util.TimeZone/getTimeZone "GMT")))))) + +(extend-protocol-fns + AsXmlString + String {:xml-str identity} + (Boolean Byte Character Short Integer Long Float Double + BigInteger BigDecimal BigInt URI URL nil) {:xml-str str}) + +(extend-protocol AsXmlString + (Class/forName "[B") + (xml-str [ba] (b64-encode ba)) + Date + (xml-str [d] (let [^DateFormat utc-format (.get thread-local-utc-date-format)] + (.format utc-format d))) + clojure.lang.Ratio + (xml-str [r] (str (.decimalValue r)))) + +(compile-if + (Class/forName "java.time.Instant") + (extend-protocol AsXmlString + java.time.Instant + (xml-str [i] (xml-str (Date/from i)))) + nil) ;; Writers diff --git a/src/main/clojure/clojure/data/xml/protocols.cljc b/src/main/clojure/clojure/data/xml/protocols.cljc index 37677b2..15e38ea 100644 --- a/src/main/clojure/clojure/data/xml/protocols.cljc +++ b/src/main/clojure/clojure/data/xml/protocols.cljc @@ -24,3 +24,6 @@ (defprotocol AsElements (as-elements [expr] "Return a seq of elements represented by an expression.")) + +(defprotocol AsXmlString + (xml-str [node] "Serialize atribute value or content node")) From e48b651cf3d768e09d1ff85044dd4ee9661e06f5 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Thu, 16 Feb 2017 17:54:11 +0100 Subject: [PATCH 151/237] DXML-25 emit empty elements --- src/main/clojure/clojure/data/xml/event.clj | 15 +++++++---- .../clojure/clojure/data/xml/jvm/emit.clj | 27 ++++++++++++------- .../clojure/clojure/data/xml/jvm/parse.clj | 23 +++++++++------- .../clojure/clojure/data/xml/test_emit.clj | 4 +++ 4 files changed, 45 insertions(+), 24 deletions(-) diff --git a/src/main/clojure/clojure/data/xml/event.clj b/src/main/clojure/clojure/data/xml/event.clj index aa7384f..1fbcf40 100644 --- a/src/main/clojure/clojure/data/xml/event.clj +++ b/src/main/clojure/clojure/data/xml/event.clj @@ -31,6 +31,7 @@ ; Represents a parse event. (defrecord StartElementEvent [tag attrs nss location-info]) +(defrecord EmptyElementEvent [tag attrs nss location-info]) (defrecord CharsEvent [str]) (defrecord CDataEvent [str]) (defrecord CommentEvent [str]) @@ -45,19 +46,22 @@ (let [second-arg #(do %2) elem-event-generation - {:gen-event (fn elem-gen-event [{:keys [tag attrs] :as element}] + {:gen-event (fn elem-gen-event [{:keys [tag attrs content] :as element}] (separate-xmlns - attrs #(->StartElementEvent + attrs #((if (seq content) + ->StartElementEvent ->EmptyElementEvent) tag %1 (merge-nss (element-nss* element) %2) nil))) :next-events (fn elem-next-events [{:keys [tag content]} next-items] - (list* content end-element-event next-items))} + (if (seq content) + (list* content end-element-event next-items) + next-items))} string-event-generation {:gen-event (comp ->CharsEvent #'xml-str) :next-events second-arg} qname-event-generation {:gen-event ->QNameEvent :next-events second-arg}] (extend-protocol-fns EventGeneration - (StartElementEvent EndElementEvent CharsEvent CDataEvent CommentEvent) + (StartElementEvent EmptyElementEvent EndElementEvent CharsEvent CDataEvent CommentEvent) {:gen-event identity :next-events second-arg} (String Boolean Number (Class/forName "[B") Date URI URL nil) @@ -89,7 +93,8 @@ ;; Node Generation for events (defn event-element [event contents] - (when (instance? StartElementEvent event) + (when (or (instance? StartElementEvent event) + (instance? EmptyElementEvent event)) (element* (:tag event) (:attrs event) contents (if-let [loc (:location-info event)] {:clojure.data.xml/location-info loc diff --git a/src/main/clojure/clojure/data/xml/jvm/emit.clj b/src/main/clojure/clojure/data/xml/jvm/emit.clj index 0a1225e..542e1ca 100644 --- a/src/main/clojure/clojure/data/xml/jvm/emit.clj +++ b/src/main/clojure/clojure/data/xml/jvm/emit.clj @@ -23,7 +23,7 @@ (javax.xml.stream XMLStreamWriter XMLOutputFactory) (javax.xml.transform OutputKeys Transformer TransformerFactory) - (clojure.data.xml.event StartElementEvent EndElementEvent CharsEvent CDataEvent CommentEvent QNameEvent) + (clojure.data.xml.event StartElementEvent EmptyElementEvent EndElementEvent CharsEvent CDataEvent CommentEvent QNameEvent) (clojure.lang BigInt) (java.net URI URL) (java.util Date) @@ -112,17 +112,24 @@ tpu)] (pu/persistent! tpu))) -(defn- emit-start-tag [{:keys [attrs nss tag]} ^XMLStreamWriter writer prefix-uri-stack] +(defn- emit-start-tag [{:keys [attrs nss tag]} ^XMLStreamWriter writer prefix-uri-stack empty] (let [uri (qname-uri tag) local (qname-local tag) parent-pu (first prefix-uri-stack) pu (compute-pu parent-pu nss (map qname-uri (keys attrs)) uri local)] - (if (str/blank? uri) - (.writeStartElement writer local) - (.writeStartElement writer (pu/get-prefix pu uri) local uri)) - (emit-ns-attrs writer parent-pu pu) - (emit-attrs writer pu attrs) - (cons pu prefix-uri-stack))) + (if empty + (do (if (str/blank? uri) + (.writeEmptyElement writer local) + (.writeEmptyElement writer (pu/get-prefix pu uri) local uri)) + (emit-ns-attrs writer parent-pu pu) + (emit-attrs writer pu attrs) + prefix-uri-stack) + (do (if (str/blank? uri) + (.writeStartElement writer local) + (.writeStartElement writer (pu/get-prefix pu uri) local uri)) + (emit-ns-attrs writer parent-pu pu) + (emit-attrs writer pu attrs) + (cons pu prefix-uri-stack))))) (defn- emit-cdata [^String cdata-str ^XMLStreamWriter writer] (when-not (str/blank? cdata-str) @@ -135,7 +142,9 @@ (extend-protocol EventEmit StartElementEvent - (emit-event [ev writer pu-stack] (emit-start-tag ev writer pu-stack)) + (emit-event [ev writer pu-stack] (emit-start-tag ev writer pu-stack false)) + EmptyElementEvent + (emit-event [ev writer pu-stack] (emit-start-tag ev writer pu-stack true)) EndElementEvent (emit-event [ev writer pu-stack] (assert (next pu-stack) "balanced tags") diff --git a/src/main/clojure/clojure/data/xml/jvm/parse.clj b/src/main/clojure/clojure/data/xml/jvm/parse.clj index 0b9c645..46614a2 100644 --- a/src/main/clojure/clojure/data/xml/jvm/parse.clj +++ b/src/main/clojure/clojure/data/xml/jvm/parse.clj @@ -9,7 +9,7 @@ (ns clojure.data.xml.jvm.parse (:require [clojure.string :as str] [clojure.data.xml.event :refer - [->StartElementEvent ->EndElementEvent + [->StartElementEvent ->EmptyElementEvent ->EndElementEvent ->CharsEvent ->CDataEvent ->CommentEvent]] [clojure.data.xml.impl :refer [static-case]] @@ -17,7 +17,8 @@ [qname]]) (:import (javax.xml.stream - XMLInputFactory XMLStreamReader XMLStreamConstants))) + XMLInputFactory XMLStreamReader XMLStreamConstants) + (clojure.data.xml.event EndElementEvent))) (def ^{:private true} input-factory-props {:allocator XMLInputFactory/ALLOCATOR @@ -76,14 +77,16 @@ (.next sreader) XMLStreamConstants/START_ELEMENT (if (include-node? :element) - (let [ns-env (nss-hash sreader (or (first ns-envs) {}))] - (cons (->StartElementEvent (qname (.getNamespaceURI sreader) - (.getLocalName sreader) - (.getPrefix sreader)) - (attr-hash sreader) - ns-env - location) - (pull-seq sreader opts (cons ns-env ns-envs)))) + (let [ns-env (nss-hash sreader (or (first ns-envs) {})) + tag (qname (.getNamespaceURI sreader) + (.getLocalName sreader) + (.getPrefix sreader)) + attrs (attr-hash sreader) + next-events (pull-seq sreader opts (cons ns-env ns-envs))] + ;; Can't emit EmptyElementEvent here, since + ;; for seq-tree node and exit? are mutually exclusive + (cons (->StartElementEvent tag attrs ns-env location) + next-events)) (recur)) XMLStreamConstants/END_ELEMENT (if (include-node? :element) diff --git a/src/test/clojure/clojure/data/xml/test_emit.clj b/src/test/clojure/clojure/data/xml/test_emit.clj index 385ed1d..0f5b382 100644 --- a/src/test/clojure/clojure/data/xml/test_emit.clj +++ b/src/test/clojure/clojure/data/xml/test_emit.clj @@ -218,3 +218,7 @@ (is (= {:xmlns "NS"} (nss-meta (parse-str "")) (nss-meta (parse-str (emit-str (parse-str "")))))))) + +(deftest test-empty-elements + (is (= (emit-str {:tag :a :content []}) "")) + (is (= (emit-str {:tag :a :content [""]}) ""))) From 0b621ebfb45eeb0ed4aeba0dc5eeeff0478f3a65 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Thu, 16 Feb 2017 17:55:11 +0100 Subject: [PATCH 152/237] Update CHANGES.md --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index c768338..cc80119 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,10 @@ From 0.2.0-alpha2 to 0.2.0-alpha3 - Minimum requirement is now clojure 1.7.0 - Print newline after preamble when pretty-printing (DXML-35) +- Serialize built-in data types in XML Schema (DXML-27) +- Reimplement namespace context tracking, due to bug in JDK +- Various fixes in documentation and error messages (DXML-39) +- Emit empty tags for elements with no content (DXML-25) From 0.2.0-alpha1 to 0.2.0-alpha2 - qname function now returns canonical (keyword) names From adfb17f3051249c327737fe28100b458ecd9d386 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Thu, 16 Feb 2017 18:48:28 +0100 Subject: [PATCH 153/237] make testing of java.time.Instant conditional --- src/test/clojure/clojure/data/xml/test_emit.clj | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/test/clojure/clojure/data/xml/test_emit.clj b/src/test/clojure/clojure/data/xml/test_emit.clj index 0f5b382..f069bee 100644 --- a/src/test/clojure/clojure/data/xml/test_emit.clj +++ b/src/test/clojure/clojure/data/xml/test_emit.clj @@ -12,7 +12,8 @@ (:require [clojure.test :refer :all] [clojure.data.xml :refer :all] - [clojure.data.xml.test-utils :refer [test-stream lazy-parse*]]) + [clojure.data.xml.test-utils :refer [test-stream lazy-parse*]] + [clojure.data.xml.impl :refer [compile-if]]) (:import (javax.xml.namespace QName))) (def deep-tree @@ -191,8 +192,13 @@ (java.net.URL. "http://foo") "http://foo") (are-serializable "dates" {} - (java.util.Date. 0) "1970-01-01T00:00:00.000-00:00" - (java.time.Instant/ofEpochMilli 0) "1970-01-01T00:00:00.000-00:00") + (java.util.Date. 0) "1970-01-01T00:00:00.000-00:00") + (compile-if + (Class/forName "java.time.Instant") + (are-serializable + "instants" {} + (java.time.Instant/ofEpochMilli 0) "1970-01-01T00:00:00.000-00:00") + nil) (are-serializable "qnames" {:xmlns/p "U:"} :xmlns.U%3A/qn "p:qn" From 967435dd9e7cce971135e7d3882d6d8b36f238c7 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Thu, 16 Feb 2017 19:04:55 +0100 Subject: [PATCH 154/237] DXML-35 alternate approach to newline after preamble this avoids an IllegalArgumentException --- src/main/clojure/clojure/data/xml/jvm/pprint.clj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/clojure/data/xml/jvm/pprint.clj b/src/main/clojure/clojure/data/xml/jvm/pprint.clj index 1c26a39..57b4ad8 100644 --- a/src/main/clojure/clojure/data/xml/jvm/pprint.clj +++ b/src/main/clojure/clojure/data/xml/jvm/pprint.clj @@ -14,11 +14,11 @@ (defn ^Transformer indenting-transformer [] (doto (-> (TransformerFactory/newInstance) .newTransformer) - (.setOutputProperty (OutputKeys/INDENT) "yes") - (.setOutputProperty (OutputKeys/METHOD) "xml") + (.setOutputProperty OutputKeys/INDENT "yes") + (.setOutputProperty OutputKeys/METHOD "xml") (.setOutputProperty "{http://xml.apache.org/xslt}indent-amount" "2") - ;; print newline after preamble https://bugs.openjdk.java.net/browse/JDK-7150637 - (.setOutputProperty "http://www.oracle.com/xml/is-standalone" "yes"))) + ;; print newline after preamble + (.setOutputProperty OutputKeys/DOCTYPE_PUBLIC "yes"))) (defn indent-xml [xml-str ^Writer writer] From c803aebfac7c2d26d7ed9e5f72ae8aaf480dbbae Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Tue, 21 Mar 2017 22:02:54 -0500 Subject: [PATCH 155/237] Update parent pom --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cb860c7..d7dcf7f 100644 --- a/pom.xml +++ b/pom.xml @@ -32,7 +32,7 @@ org.clojure pom.contrib - 0.2.0 + 0.2.2 From ef89c7ab275e3681f9ddf574b78cd9eb491d115c Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Sat, 8 Apr 2017 00:18:49 +0200 Subject: [PATCH 156/237] add element? predicate --- src/main/clojure/clojure/data/xml.clj | 2 +- src/main/clojure/clojure/data/xml/node.cljc | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/clojure/data/xml.clj b/src/main/clojure/clojure/data/xml.clj index de66512..49cf334 100644 --- a/src/main/clojure/clojure/data/xml.clj +++ b/src/main/clojure/clojure/data/xml.clj @@ -31,7 +31,7 @@ [clojure.data.xml.tree :refer [event-tree flatten-elements]])) -(export-api node/element* node/element node/cdata node/xml-comment +(export-api node/element* node/element node/cdata node/xml-comment node/element? prxml/sexp-as-element prxml/sexps-as-fragment event/element-nss name/alias-uri name/parse-qname name/qname-uri name/qname-local name/qname name/as-qname name/uri-symbol name/symbol-uri diff --git a/src/main/clojure/clojure/data/xml/node.cljc b/src/main/clojure/clojure/data/xml/node.cljc index 680151c..d811c54 100644 --- a/src/main/clojure/clojure/data/xml/node.cljc +++ b/src/main/clojure/clojure/data/xml/node.cljc @@ -217,3 +217,6 @@ ;; TODO support hiccup syntax :else (throw (ex-info "Unsupported element representation" {:element el})))) + +(defn element? [el] + (and (map? el) (some? (:tag el)))) From 5ff464c72cb2a59d7ec45d16790a3c9d5be61309 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Sat, 8 Apr 2017 00:18:59 +0200 Subject: [PATCH 157/237] DXML-44 support empty on elements --- src/main/clojure/clojure/data/xml/node.cljc | 2 ++ src/test/clojure/clojure/data/xml/test_process.clj | 13 ++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/clojure/data/xml/node.cljc b/src/main/clojure/clojure/data/xml/node.cljc index d811c54..fafd89a 100644 --- a/src/main/clojure/clojure/data/xml/node.cljc +++ b/src/main/clojure/clojure/data/xml/node.cljc @@ -119,6 +119,8 @@ :clj [(seq [this] (iterator-seq (.iterator this)))]) + #?(:clj (empty [_] (Element. tag {} [] {}))) + ;; j.u.Map and included interfaces #?@(:clj [Map diff --git a/src/test/clojure/clojure/data/xml/test_process.clj b/src/test/clojure/clojure/data/xml/test_process.clj index bef4522..afe7709 100644 --- a/src/test/clojure/clojure/data/xml/test_process.clj +++ b/src/test/clojure/clojure/data/xml/test_process.clj @@ -1,6 +1,8 @@ (ns clojure.data.xml.test-process (:require [clojure.data.xml :refer :all] - [clojure.test :refer :all])) + [clojure.test :refer :all] + [clojure.walk :as w] + [clojure.string :as str])) (def test-data (element @@ -14,3 +16,12 @@ (deftest process (is (= (find-xmlns test-data) #{"" "GEE:" "GOO:"})) (is (= (set (vals (element-nss (aggregate-xmlns test-data)))) #{"GEE:" "GOO:"}))) + +(deftest walk-test + (is (= {:tag :FOO, :attrs {}, :content ()} + (w/postwalk (fn [e] + (println e) + (if (element? e) + (update e :tag (comp keyword str/upper-case name)) + e)) + (element :foo))))) From b7878f98a1fd1cad917ff932a97486aad9fc63e2 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Mon, 10 Apr 2017 00:44:37 +0200 Subject: [PATCH 158/237] README reformat and clarification --- README.md | 50 ++++++++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index ec42f91..2e58130 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ data.xml has the following features: * Parses XML documents into Clojure data structures * Emits XML from Clojure data structures -* No additional dependencies if using 1.6 +* No additional dependencies if using JDK >= 1.6 * Uses StAX internally * lazy - should allow parsing and emitting of large XML documents @@ -17,7 +17,11 @@ Please report bugs using JIRA [here](http://dev.clojure.org/jira/browse/DXML). ## Installation -Latest stable release: 0.0.8 +Latest stable release: `0.0.8` + +Latest preview release: `0.2.0-alpha3` + +(The main features of the `0.2.0` series are XML Namespace support and Clojurescript support) * [All Released Versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22data.xml%22) @@ -26,44 +30,42 @@ Latest stable release: 0.0.8 ### Maven For Maven projects, add the following XML in your `pom.xml`'s `` section: + For stable: + org.clojure data.xml 0.0.8 -### Leiningen -Add the following to the `project.clj` dependencies: - - [org.clojure/data.xml "0.0.8"] - -## Installation - Alpha - -Latest alpha release: 0.2.0-alpha2 - -* [All Released Versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22data.xml%22) - -* [Development Snapshot Versions](https://oss.sonatype.org/index.html#nexus-search;gav~org.clojure~data.xml~~~) +--- -### Maven -For Maven projects, add the following XML in your `pom.xml`'s `` section: + For preview: org.clojure data.xml - 0.2.0-alpha2 + 0.2.0-alpha3 ### Leiningen Add the following to the `project.clj` dependencies: - [org.clojure/data.xml "0.2.0-alpha2"] + For stable: + + [org.clojure/data.xml "0.0.8"] + +--- + + For preview: + + [org.clojure/data.xml "0.2.0-alpha3"] ## Examples -The examples below assume you have added a `use` for data.xml: +The examples below assume you have added a `:refer :all` for data.xml: - (use 'clojure.data.xml) + (require '[clojure.data.xml :refer :all]) data.xml supports parsing and emitting XML. The parsing functions will read XML from a @@ -202,7 +204,7 @@ Below is an example of parsing an XHTML document: (parse-str " ") - + #...Element{:tag :xmlns.http%3A%2F%2Fwww.w3.org%2F1999%2Fxhtml/html, :attrs {}, :content ()} @@ -210,7 +212,7 @@ Below is an example of parsing an XHTML document: Emitting namespaced XML is usually done by using `alias-uri` in combination with clojure's built-in `::kw-ns/shorthands`: (alias-uri 'xh "http://www.w3.org/1999/xhtml") - + (emit-str {:tag ::xh/html :content [{:tag ::xh/head} {:tag ::xh/body :content ["DOCUMENT"]}]}) @@ -224,7 +226,7 @@ It is also allowable to use `javax.xml.namespace.QName` instances, as well as st (emit-str {:tag (qname "http://www.w3.org/1999/xhtml" "html")}) (emit-str {:tag "{http://www.w3.org/1999/xhtml}html"}) - + ### Namespace Prefixes @@ -284,7 +286,7 @@ By default the parser attaches location information as element meta, (let [input "\n" location-meta (comp :clojure.data.xml/location-info meta)] (is (= 1 (-> input parse-str location-meta :line-number))) - + To elide location information, pass `:location-info false` to the parser: (parse-str your-input :location-info false) From 34da1a46d71084b3c3a912b315e73382e5ddd001 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Mon, 10 Apr 2017 01:03:08 +0200 Subject: [PATCH 159/237] DXML-44 cljs support for empty on elements --- src/main/clojure/clojure/data/xml.cljs | 2 +- src/main/clojure/clojure/data/xml/node.cljc | 3 +++ .../xml/{test_process.clj => test_process.cljc} | 15 +++++++++------ .../clojurescript/clojure/data/xml/test_cljs.cljs | 6 ++++-- 4 files changed, 17 insertions(+), 9 deletions(-) rename src/test/clojure/clojure/data/xml/{test_process.clj => test_process.cljc} (60%) diff --git a/src/main/clojure/clojure/data/xml.cljs b/src/main/clojure/clojure/data/xml.cljs index b4e768b..e54f463 100644 --- a/src/main/clojure/clojure/data/xml.cljs +++ b/src/main/clojure/clojure/data/xml.cljs @@ -9,7 +9,7 @@ (export-api name/parse-qname name/qname-uri name/qname-local name/qname name/as-qname name/uri-symbol name/symbol-uri - node/element* node/element node/cdata node/xml-comment + node/element* node/element node/cdata node/xml-comment node/element? dom/extend-dom-as-data! dom/element-node dom/element-data) ;;;; ## TODO event-seq diff --git a/src/main/clojure/clojure/data/xml/node.cljc b/src/main/clojure/clojure/data/xml/node.cljc index fafd89a..201c062 100644 --- a/src/main/clojure/clojure/data/xml/node.cljc +++ b/src/main/clojure/clojure/data/xml/node.cljc @@ -120,6 +120,9 @@ [(seq [this] (iterator-seq (.iterator this)))]) #?(:clj (empty [_] (Element. tag {} [] {}))) + #?@(:cljs + [IEmptyableCollection + (-empty [_] (Element. tag {} [] {}))]) ;; j.u.Map and included interfaces #?@(:clj diff --git a/src/test/clojure/clojure/data/xml/test_process.clj b/src/test/clojure/clojure/data/xml/test_process.cljc similarity index 60% rename from src/test/clojure/clojure/data/xml/test_process.clj rename to src/test/clojure/clojure/data/xml/test_process.cljc index afe7709..e476e5d 100644 --- a/src/test/clojure/clojure/data/xml/test_process.clj +++ b/src/test/clojure/clojure/data/xml/test_process.cljc @@ -1,6 +1,8 @@ (ns clojure.data.xml.test-process - (:require [clojure.data.xml :refer :all] - [clojure.test :refer :all] + (:require [clojure.data.xml :refer [element qname element? + #?@(:clj [element-nss aggregate-xmlns + find-xmlns])]] + [clojure.test :refer [deftest is]] [clojure.walk :as w] [clojure.string :as str])) @@ -13,14 +15,15 @@ (element (qname "GOO:" "ho") {(qname "GEE:" "hi") "ma"} "ii") "end")) -(deftest process - (is (= (find-xmlns test-data) #{"" "GEE:" "GOO:"})) - (is (= (set (vals (element-nss (aggregate-xmlns test-data)))) #{"GEE:" "GOO:"}))) +#? +(:clj + (deftest process + (is (= (find-xmlns test-data) #{"" "GEE:" "GOO:"})) + (is (= (set (vals (element-nss (aggregate-xmlns test-data)))) #{"GEE:" "GOO:"})))) (deftest walk-test (is (= {:tag :FOO, :attrs {}, :content ()} (w/postwalk (fn [e] - (println e) (if (element? e) (update e :tag (comp keyword str/upper-case name)) e)) diff --git a/src/test/clojurescript/clojure/data/xml/test_cljs.cljs b/src/test/clojurescript/clojure/data/xml/test_cljs.cljs index e05d837..9b96c37 100644 --- a/src/test/clojurescript/clojure/data/xml/test_cljs.cljs +++ b/src/test/clojurescript/clojure/data/xml/test_cljs.cljs @@ -4,7 +4,8 @@ clojure.data.xml.test-cljs-basic clojure.data.xml.test-cljs-extended clojure.data.xml.test-equiv - clojure.data.xml.test-pu)) + clojure.data.xml.test-pu + clojure.data.xml.test-process)) (def ^:dynamic *results*) @@ -21,7 +22,8 @@ (println "Running Basic Tests") (test/run-tests 'clojure.data.xml.test-cljs-basic 'clojure.data.xml.test-equiv - 'clojure.data.xml.test-pu) + 'clojure.data.xml.test-pu + 'clojure.data.xml.test-process) (pr-str *results*))) (defn ^:export -main [] From c6871e6deed688c51172dfb40b41e1aa1d303400 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Sun, 18 Jun 2017 22:20:11 +0200 Subject: [PATCH 160/237] update CHANGES and dependencies --- CHANGES.md | 2 ++ pom.xml | 2 +- project.clj | 10 +++++----- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index cc80119..372e183 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,6 +5,8 @@ From 0.2.0-alpha2 to 0.2.0-alpha3 - Reimplement namespace context tracking, due to bug in JDK - Various fixes in documentation and error messages (DXML-39) - Emit empty tags for elements with no content (DXML-25) +- Add clojure.data.xml/element? predicate +- Support empty protocol function on Element deftypes (DXML-44) From 0.2.0-alpha1 to 0.2.0-alpha2 - qname function now returns canonical (keyword) names diff --git a/pom.xml b/pom.xml index d7dcf7f..eb8e5fb 100644 --- a/pom.xml +++ b/pom.xml @@ -51,7 +51,7 @@ org.clojure clojurescript - 1.9.473 + 1.9.562 test diff --git a/project.clj b/project.clj index a2b5e1b..a0245bc 100644 --- a/project.clj +++ b/project.clj @@ -4,10 +4,10 @@ :resource-paths ["src/test/resources" "target/gen-resources"] :dependencies [[org.clojure/clojure "1.8.0"] [org.clojure/data.codec "0.1.0"] - [org.clojure/clojurescript "1.9.473"] - [com.cemerick/piggieback "0.2.1"] - [org.clojure/tools.nrepl "0.2.12"] + [org.clojure/clojurescript "1.9.562"] + [com.cemerick/piggieback "0.2.2"] + [org.clojure/tools.nrepl "0.2.13"] [org.clojure/test.check "0.9.0"] - [figwheel-sidecar "0.5.8"] - [binaryage/devtools "0.8.3"]] + [figwheel-sidecar "0.5.10"] + [binaryage/devtools "0.9.4"]] :repl-options {:nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]}) From ce63e8ef8116b8918117e2da35885b4c270ba168 Mon Sep 17 00:00:00 2001 From: Michael du Breuil Date: Sat, 17 Jun 2017 00:38:52 -0700 Subject: [PATCH 161/237] DXML-42 Reflection cleanup --- src/main/clojure/clojure/data/xml/jvm/emit.clj | 12 ++++++------ src/main/clojure/clojure/data/xml/jvm/parse.clj | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/clojure/clojure/data/xml/jvm/emit.clj b/src/main/clojure/clojure/data/xml/jvm/emit.clj index 542e1ca..822f51d 100644 --- a/src/main/clojure/clojure/data/xml/jvm/emit.clj +++ b/src/main/clojure/clojure/data/xml/jvm/emit.clj @@ -102,8 +102,8 @@ tpu (if-let [uri (and (str/blank? tag-uri) (pu/get tpu ""))] (do - (when (.isLoggable logger Level/FINE) - (.log logger Level/FINE + (when (.isLoggable ^Logger logger Level/FINE) + (.log ^Logger logger Level/FINE (format "Default `xmlns=\"%s\"` had to be replaced with a `xmlns=\"\"` because of global element `%s`" uri tag-local))) (-> tpu @@ -148,16 +148,16 @@ EndElementEvent (emit-event [ev writer pu-stack] (assert (next pu-stack) "balanced tags") - (.writeEndElement writer) + (.writeEndElement ^XMLStreamWriter writer) (next pu-stack)) CharsEvent - (emit-event [{:keys [str]} writer s] (.writeCharacters writer str) s) + (emit-event [{:keys [str]} writer s] (.writeCharacters ^XMLStreamWriter writer str) s) CDataEvent (emit-event [{:keys [str]} writer s] (emit-cdata str writer) s) CommentEvent - (emit-event [{:keys [str]} writer s] (.writeComment writer str) s) + (emit-event [{:keys [str]} writer s] (.writeComment ^XMLStreamWriter writer str) s) QNameEvent - (emit-event [{:keys [qn]} writer pu-stack] + (emit-event [{:keys [qn]} ^XMLStreamWriter writer pu-stack] (.writeCharacters writer (prefix-for qn (first pu-stack))) (.writeCharacters writer ":") (.writeCharacters writer (qname-local qn)) diff --git a/src/main/clojure/clojure/data/xml/jvm/parse.clj b/src/main/clojure/clojure/data/xml/jvm/parse.clj index 46614a2..4894c12 100644 --- a/src/main/clojure/clojure/data/xml/jvm/parse.clj +++ b/src/main/clojure/clojure/data/xml/jvm/parse.clj @@ -48,11 +48,11 @@ (defn- nss-hash [^XMLStreamReader sreader parent-hash] (persistent! - (reduce (fn [tr i] + (reduce (fn [tr ^long i] (let [ns-pf (.getNamespacePrefix sreader i)] (assoc! tr (if (str/blank? ns-pf) :xmlns ns-pf) - (.getNamespaceURI sreader i)))) + (.getNamespaceURI ^XMLStreamReader sreader i)))) (transient parent-hash) (range (.getNamespaceCount sreader))))) From 5b13e2b2e8cb5445ac6c5f6926f3591b10eba66d Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Mon, 3 Jul 2017 19:24:32 +0200 Subject: [PATCH 162/237] prepare documentation for release --- CHANGES.md | 1 + README.md | 15 ++--- src/main/clojure/clojure/data/xml.clj | 94 ++++++++++++++++++++++----- 3 files changed, 84 insertions(+), 26 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 372e183..f4234d6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -7,6 +7,7 @@ From 0.2.0-alpha2 to 0.2.0-alpha3 - Emit empty tags for elements with no content (DXML-25) - Add clojure.data.xml/element? predicate - Support empty protocol function on Element deftypes (DXML-44) +- Reflection cleanup (DXML-42) From 0.2.0-alpha1 to 0.2.0-alpha2 - qname function now returns canonical (keyword) names diff --git a/README.md b/README.md index 2e58130..4612542 100644 --- a/README.md +++ b/README.md @@ -198,7 +198,7 @@ Generated API docs for data.xml are available [here](http://clojure.github.com/d ## Namespace Support -XML Namespaced names (QNames) are commonly encoded into clojure keywords, by percent-encoding the (XML) namespace: `{http://www.w3.org/1999/xhtml}head` is encoded in data.xml as `:http%3A%2F%2Fwww.w3.org%2F1999%2Fxhtml/head`. +XML Namespaced names (QNames) are encoded into clojure keywords, by percent-encoding the (XML) namespace: `{http://www.w3.org/1999/xhtml}head` is encoded in data.xml as `:http%3A%2F%2Fwww.w3.org%2F1999%2Fxhtml/head`. Below is an example of parsing an XHTML document: @@ -262,19 +262,14 @@ tags with the same namespace will use one prefix: "Example title" -Note that the Java QName does not consider namespace prefixes when -checking equality. Similarly constructing QNames from string -representations does not preserve prefixes. Prefixes are treated -similarly in data.xml. Prefixes are currently represented as metadata -on the elements. This preserves the same equality behavior that QNames -have: +Note that the jdk QName ignores namespace prefixes for equality, but allows to preserve them for emitting. (= (parse-str "Example title") (parse-str "Example title")) -Removing the metadata will cause the elements to not have a prefix, -which is still correct, but will cause new prefixes to be generated -when the document is emitted. +In data.xml prefix mappings are (by default) retained in metadata on a tag record. If there is no metadata, new prefixes will be generated when emitting. + + (emit-str (parse-str "")) ## Location information as meta diff --git a/src/main/clojure/clojure/data/xml.clj b/src/main/clojure/clojure/data/xml.clj index 49cf334..9552130 100644 --- a/src/main/clojure/clojure/data/xml.clj +++ b/src/main/clojure/clojure/data/xml.clj @@ -38,35 +38,97 @@ name/uri-file name/print-uri-file-command! process/find-xmlns process/aggregate-xmlns) +(def ^:private ^:const parser-opts-arg + '{:keys [include-node? location-info + coalescing supporting-external-entities + allocator namespace-aware replacing-entity-references + validating reporter resolver support-dtd] + :or {include-node? #{:element :characters} + location-info true + coalescing true + supporting-external-entities false}}) + (defn event-seq - "Parses the XML InputSource source using a pull-parser. Returns - a lazy sequence of Event records. Accepts key pairs - with XMLInputFactory options, see http://docs.oracle.com/javase/6/docs/api/javax/xml/stream/XMLInputFactory.html - and xml-input-factory-props for more information. - Defaults coalescing true and supporting-external-entities false. - :include-node? can be a set of #{:start-element :end-element :characters :comment}" - [source {:as props}] + "Parses an XML input source into a lazy sequence of pull events. + +Input source can be a java.io.InputStream or java.io.Reader + +Options: + + :include-node? can be a subset of #{:element :characters :comment} default #{:element :characters} + :location-info pass false to skip generating location meta data + +See http://docs.oracle.com/javase/6/docs/api/javax/xml/stream/XMLInputFactory.html +for documentation on options: + + {:allocator XMLInputFactory/ALLOCATOR + :coalescing XMLInputFactory/IS_COALESCING + :namespace-aware XMLInputFactory/IS_NAMESPACE_AWARE + :replacing-entity-references XMLInputFactory/IS_REPLACING_ENTITY_REFERENCES + :supporting-external-entities XMLInputFactory/IS_SUPPORTING_EXTERNAL_ENTITIES + :validating XMLInputFactory/IS_VALIDATING + :reporter XMLInputFactory/REPORTER + :resolver XMLInputFactory/RESOLVER + :support-dtd XMLInputFactory/SUPPORT_DTD}" + {:arglists (list ['source parser-opts-arg])} + [source opts] (let [props* (merge {:include-node? #{:element :characters} :coalescing true :supporting-external-entities false :location-info true} - props)] + opts)] (pull-seq (make-stream-reader props* source) props* nil))) (defn parse - "Parses the source, which can be an - InputStream or Reader, and returns a lazy tree of Element records. Accepts key pairs - with XMLInputFactory options, see http://docs.oracle.com/javase/6/docs/api/javax/xml/stream/XMLInputFactory.html - and xml-input-factory-props for more information. Defaults coalescing true." - [source & opts] + "Parses an XML input source into a a tree of Element records. +The element tree is realized lazily, so huge XML files can be streamed through a depth-first tree walk. + +Input source can be a java.io.InputStream or java.io.Reader + +Options: + + :include-node? can be a subset of #{:element :characters :comment} default #{:element :characters} + :location-info pass false to skip generating location meta data + +See http://docs.oracle.com/javase/6/docs/api/javax/xml/stream/XMLInputFactory.html +for documentation on options: + + {:allocator XMLInputFactory/ALLOCATOR + :coalescing XMLInputFactory/IS_COALESCING + :namespace-aware XMLInputFactory/IS_NAMESPACE_AWARE + :replacing-entity-references XMLInputFactory/IS_REPLACING_ENTITY_REFERENCES + :supporting-external-entities XMLInputFactory/IS_SUPPORTING_EXTERNAL_ENTITIES + :validating XMLInputFactory/IS_VALIDATING + :reporter XMLInputFactory/REPORTER + :resolver XMLInputFactory/RESOLVER + :support-dtd XMLInputFactory/SUPPORT_DTD}" + {:arglists (list ['source '& parser-opts-arg])} + [source & {:as opts}] (event-tree (event-seq source opts))) (defn parse-str - "Parses the passed in string to Clojure data structures. Accepts key pairs - with XMLInputFactory options, see http://docs.oracle.com/javase/6/docs/api/javax/xml/stream/XMLInputFactory.html - and xml-input-factory-props for more information. Defaults coalescing true." + "Parses an XML String into a a tree of Element records. + +Options: + + :include-node? can be a subset of #{:element :characters :comment} default #{:element :characters} + :location-info pass false to skip generating location meta data + +See http://docs.oracle.com/javase/6/docs/api/javax/xml/stream/XMLInputFactory.html +for documentation on options: + + {:allocator XMLInputFactory/ALLOCATOR + :coalescing XMLInputFactory/IS_COALESCING + :namespace-aware XMLInputFactory/IS_NAMESPACE_AWARE + :replacing-entity-references XMLInputFactory/IS_REPLACING_ENTITY_REFERENCES + :supporting-external-entities XMLInputFactory/IS_SUPPORTING_EXTERNAL_ENTITIES + :validating XMLInputFactory/IS_VALIDATING + :reporter XMLInputFactory/REPORTER + :resolver XMLInputFactory/RESOLVER + :support-dtd XMLInputFactory/SUPPORT_DTD}" + {:arglists (list ['string '& parser-opts-arg])} [s & opts] (apply parse (string-source s) opts)) From efdfe9743965c3818950b25bc3081d3f426e14cc Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Wed, 27 Sep 2017 01:09:41 +0200 Subject: [PATCH 163/237] fix round-trip prefix assignments this cleans up usage of metadata to always store strings into nss-attrs this way, our emitter can restore prefixes and default xmlns, exactly as they appeared in a source --- src/main/clojure/clojure/data/xml/jvm/emit.clj | 11 ++++++----- src/main/clojure/clojure/data/xml/jvm/parse.clj | 2 +- src/main/clojure/clojure/data/xml/name.cljc | 7 +++---- src/test/clojure/clojure/data/xml/test_emit.clj | 9 ++++++++- 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/main/clojure/clojure/data/xml/jvm/emit.clj b/src/main/clojure/clojure/data/xml/jvm/emit.clj index 822f51d..82bf35b 100644 --- a/src/main/clojure/clojure/data/xml/jvm/emit.clj +++ b/src/main/clojure/clojure/data/xml/jvm/emit.clj @@ -82,14 +82,15 @@ (defn- compute-pu [pu ns-attrs attr-uris tag-uri tag-local] (let [tpu (pu/transient pu) + ;; add xmlns, if exists + tpu (if-let [uri (get ns-attrs "xmlns")] + (pu/assoc! tpu "" uri) + tpu) ;; add namespaces from current environment tpu (reduce-kv (fn [tpu ns-attr uri] + (assert (string? ns-attr) (pr-str ns-attr)) (pu/assoc! tpu - (if (str/blank? (qname-uri ns-attr)) - (do (assert (= "xmlns" (qname-local ns-attr)) - "non-prefixed attribute, that's not xmlns= is not a namespace attr") - "") - (compute-prefix tpu uri (qname-local ns-attr))) + (compute-prefix tpu uri ns-attr) uri)) tpu ns-attrs) ;; add implicit namespaces used by tag, attrs diff --git a/src/main/clojure/clojure/data/xml/jvm/parse.clj b/src/main/clojure/clojure/data/xml/jvm/parse.clj index 4894c12..66a8948 100644 --- a/src/main/clojure/clojure/data/xml/jvm/parse.clj +++ b/src/main/clojure/clojure/data/xml/jvm/parse.clj @@ -51,7 +51,7 @@ (reduce (fn [tr ^long i] (let [ns-pf (.getNamespacePrefix sreader i)] (assoc! tr (if (str/blank? ns-pf) - :xmlns ns-pf) + "xmlns" ns-pf) (.getNamespaceURI ^XMLStreamReader sreader i)))) (transient parent-hash) (range (.getNamespaceCount sreader))))) diff --git a/src/main/clojure/clojure/data/xml/name.cljc b/src/main/clojure/clojure/data/xml/name.cljc index 8200769..5435447 100644 --- a/src/main/clojure/clojure/data/xml/name.cljc +++ b/src/main/clojure/clojure/data/xml/name.cljc @@ -141,11 +141,10 @@ (defn xmlns-attr? "Is this qname an xmlns declaration?" [qn] - (let [uri (qname-uri qn) - local (qname-local qn)] + (let [uri (qname-uri qn)] (or (= xmlns-uri uri) (and (str/blank? uri) - (= "xmlns" local))))) + (= "xmlns" (qname-local qn)))))) (defn separate-xmlns "Call cont with two args: attributes and xmlns attributes" @@ -157,7 +156,7 @@ (let [val (get attrs qn)] (if (xmlns-attr? qn) (recur attrs* - (assoc! xmlns* qn val) + (assoc! xmlns* (qname-local qn) val) (next attrs')) (recur (assoc! attrs* qn val) xmlns* diff --git a/src/test/clojure/clojure/data/xml/test_emit.clj b/src/test/clojure/clojure/data/xml/test_emit.clj index f069bee..974dfcb 100644 --- a/src/test/clojure/clojure/data/xml/test_emit.clj +++ b/src/test/clojure/clojure/data/xml/test_emit.clj @@ -221,10 +221,17 @@ (deftest test-default-xmlns (let [nss-meta (comp :clojure.data.xml/nss meta)] - (is (= {:xmlns "NS"} + (is (= {"xmlns" "NS"} (nss-meta (parse-str "")) (nss-meta (parse-str (emit-str (parse-str "")))))))) (deftest test-empty-elements (is (= (emit-str {:tag :a :content []}) "")) (is (= (emit-str {:tag :a :content [""]}) ""))) + +(deftest test-roundtrip + (is (= (emit-str (with-meta (parse-str "") + nil)) + "")) + (is (= (emit-str (parse-str "")) + ""))) From 943e193ec8cea56a02a5b6294488fddda22574e0 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Wed, 27 Sep 2017 01:17:16 +0200 Subject: [PATCH 164/237] clojurescript[test]: 1.9.562 -> 1.9.908 this should make the test matrix happy again --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index eb8e5fb..3163802 100644 --- a/pom.xml +++ b/pom.xml @@ -51,7 +51,7 @@ org.clojure clojurescript - 1.9.562 + 1.9.908 test From 5e89b53ecdc01223633702d9cb592686689111b3 Mon Sep 17 00:00:00 2001 From: "Hudson @ build.clojure.org" Date: Tue, 26 Sep 2017 18:55:29 -0500 Subject: [PATCH 165/237] [maven-release-plugin] prepare release data.xml-0.2.0-alpha3 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 3163802..163e125 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ 4.0.0 data.xml - 0.2.0-SNAPSHOT + 0.2.0-alpha3 data.xml jar Functions to parse XML into lazy sequences and lazy trees and emit these as text @@ -85,7 +85,7 @@ scm:git:git@github.com:clojure/data.xml.git scm:git:git@github.com:clojure/data.xml.git git@github.com:clojure/data.xml.git - HEAD + data.xml-0.2.0-alpha3 From 2a218f9ef242bab1f0e9889be3c3a9c4e3cd898f Mon Sep 17 00:00:00 2001 From: "Hudson @ build.clojure.org" Date: Tue, 26 Sep 2017 18:55:29 -0500 Subject: [PATCH 166/237] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 163e125..3163802 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ 4.0.0 data.xml - 0.2.0-alpha3 + 0.2.0-SNAPSHOT data.xml jar Functions to parse XML into lazy sequences and lazy trees and emit these as text @@ -85,7 +85,7 @@ scm:git:git@github.com:clojure/data.xml.git scm:git:git@github.com:clojure/data.xml.git git@github.com:clojure/data.xml.git - data.xml-0.2.0-alpha3 + HEAD From 21a0e8dec6a355077c8ec5a2a46d95f1a0bfdbb9 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Wed, 27 Sep 2017 02:21:11 +0200 Subject: [PATCH 167/237] raise visibility of API reference closes #11 --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4612542..c5a0df8 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,10 @@ data.xml has the following features: * Uses StAX internally * lazy - should allow parsing and emitting of large XML documents +## API Reference + +Generated API docs for data.xml are available [here](http://clojure.github.com/data.xml). + ## Bugs Please report bugs using JIRA [here](http://dev.clojure.org/jira/browse/DXML). @@ -194,8 +198,6 @@ But are ignored when read: "and another element" -Generated API docs for data.xml are available [here](http://clojure.github.com/data.xml). - ## Namespace Support XML Namespaced names (QNames) are encoded into clojure keywords, by percent-encoding the (XML) namespace: `{http://www.w3.org/1999/xhtml}head` is encoded in data.xml as `:http%3A%2F%2Fwww.w3.org%2F1999%2Fxhtml/head`. From 60ba605b4f66e2ffb32e70bbadde0201f25e552d Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Tue, 24 Oct 2017 19:30:46 +0200 Subject: [PATCH 168/237] DXML-49: pervasively use pu-map The ticket described a failure of using the builtin prefix xml:. The reason for this commit being so large is, that in the process of fixing it, I discovered that half of the system was still using an old method of keeping track of the xmlns environment. Properly fixing this simplified the whole library a lot and removed lots of workarounds, obsoleted by pu-map. --- src/main/clojure/clojure/data/xml/event.clj | 11 ++-- .../clojure/clojure/data/xml/jvm/emit.clj | 10 +-- .../clojure/clojure/data/xml/jvm/parse.clj | 16 ++--- src/main/clojure/clojure/data/xml/name.cljc | 39 +++++++----- src/main/clojure/clojure/data/xml/process.clj | 19 +++--- src/main/clojure/clojure/data/xml/pu_map.cljc | 62 ++++++++++++------- .../clojure/clojure/data/xml/test_emit.clj | 33 ++++++++-- .../clojure/data/xml/test_process.cljc | 8 ++- .../clojure/clojure/data/xml/test_pu.cljc | 32 +++++++++- 9 files changed, 152 insertions(+), 78 deletions(-) diff --git a/src/main/clojure/clojure/data/xml/event.clj b/src/main/clojure/clojure/data/xml/event.clj index 1fbcf40..0e5166e 100644 --- a/src/main/clojure/clojure/data/xml/event.clj +++ b/src/main/clojure/clojure/data/xml/event.clj @@ -11,9 +11,10 @@ {:author "Herwig Hochleitner"} (:require [clojure.data.xml.protocols :refer [EventGeneration gen-event next-events xml-str]] - [clojure.data.xml.name :refer [merge-nss separate-xmlns]] + [clojure.data.xml.name :refer [separate-xmlns]] [clojure.data.xml.node :refer [element* cdata xml-comment]] - [clojure.data.xml.impl :refer [extend-protocol-fns compile-if]]) + [clojure.data.xml.impl :refer [extend-protocol-fns compile-if]] + [clojure.data.xml.pu-map :as pu]) (:import (clojure.data.xml.node Element CData Comment) (clojure.lang Sequential IPersistentMap Keyword) (java.net URI URL) @@ -21,13 +22,13 @@ (javax.xml.namespace QName))) (definline element-nss* [element] - `(get (meta ~element) :clojure.data.xml/nss {})) + `(get (meta ~element) :clojure.data.xml/nss pu/EMPTY)) (defn element-nss "Get xmlns environment from element" [{:keys [attrs] :as element}] (separate-xmlns - attrs #(merge-nss (element-nss* element) %2))) + attrs #(pu/merge-prefix-map (element-nss* element) %2))) ; Represents a parse event. (defrecord StartElementEvent [tag attrs nss location-info]) @@ -50,7 +51,7 @@ (separate-xmlns attrs #((if (seq content) ->StartElementEvent ->EmptyElementEvent) - tag %1 (merge-nss (element-nss* element) %2) nil))) + tag %1 (pu/merge-prefix-map (element-nss* element) %2) nil))) :next-events (fn elem-next-events [{:keys [tag content]} next-items] (if (seq content) (list* content end-element-event next-items) diff --git a/src/main/clojure/clojure/data/xml/jvm/emit.clj b/src/main/clojure/clojure/data/xml/jvm/emit.clj index 82bf35b..0df3967 100644 --- a/src/main/clojure/clojure/data/xml/jvm/emit.clj +++ b/src/main/clojure/clojure/data/xml/jvm/emit.clj @@ -80,19 +80,15 @@ (recur (gen-prefix)) prefix)))) -(defn- compute-pu [pu ns-attrs attr-uris tag-uri tag-local] +(defn- compute-pu [pu elem-pu attr-uris tag-uri tag-local] (let [tpu (pu/transient pu) - ;; add xmlns, if exists - tpu (if-let [uri (get ns-attrs "xmlns")] - (pu/assoc! tpu "" uri) - tpu) ;; add namespaces from current environment tpu (reduce-kv (fn [tpu ns-attr uri] - (assert (string? ns-attr) (pr-str ns-attr)) + (assert (string? ns-attr) (pr-str ns-attr uri)) (pu/assoc! tpu (compute-prefix tpu uri ns-attr) uri)) - tpu ns-attrs) + tpu (pu/prefix-map elem-pu)) ;; add implicit namespaces used by tag, attrs tpu (reduce (fn [tpu uri] (pu/assoc! tpu (compute-prefix tpu uri nil) uri)) diff --git a/src/main/clojure/clojure/data/xml/jvm/parse.clj b/src/main/clojure/clojure/data/xml/jvm/parse.clj index 66a8948..0be4431 100644 --- a/src/main/clojure/clojure/data/xml/jvm/parse.clj +++ b/src/main/clojure/clojure/data/xml/jvm/parse.clj @@ -14,7 +14,8 @@ [clojure.data.xml.impl :refer [static-case]] [clojure.data.xml.name :refer - [qname]]) + [qname]] + [clojure.data.xml.pu-map :as pu]) (:import (javax.xml.stream XMLInputFactory XMLStreamReader XMLStreamConstants) @@ -47,13 +48,12 @@ (range (.getAttributeCount sreader))))) (defn- nss-hash [^XMLStreamReader sreader parent-hash] - (persistent! + (pu/persistent! (reduce (fn [tr ^long i] - (let [ns-pf (.getNamespacePrefix sreader i)] - (assoc! tr (if (str/blank? ns-pf) - "xmlns" ns-pf) - (.getNamespaceURI ^XMLStreamReader sreader i)))) - (transient parent-hash) + (pu/assoc! tr + (.getNamespacePrefix sreader i) + (.getNamespaceURI ^XMLStreamReader sreader i))) + (pu/transient parent-hash) (range (.getNamespaceCount sreader))))) (defn- location-hash @@ -77,7 +77,7 @@ (.next sreader) XMLStreamConstants/START_ELEMENT (if (include-node? :element) - (let [ns-env (nss-hash sreader (or (first ns-envs) {})) + (let [ns-env (nss-hash sreader (or (first ns-envs) pu/EMPTY)) tag (qname (.getNamespaceURI sreader) (.getLocalName sreader) (.getPrefix sreader)) diff --git a/src/main/clojure/clojure/data/xml/name.cljc b/src/main/clojure/clojure/data/xml/name.cljc index 5435447..76edbe9 100644 --- a/src/main/clojure/clojure/data/xml/name.cljc +++ b/src/main/clojure/clojure/data/xml/name.cljc @@ -127,17 +127,6 @@ (alias al xn) (recur rst)))))) -(defn merge-nss - "Merge two attribute sets, deleting assignments of empty-string" - [nss1 nss2] - (persistent! - (reduce-kv (fn [a k v] - (if (str/blank? v) - (dissoc! a k) - (assoc! a k v))) - (transient nss1) - nss2))) - (defn xmlns-attr? "Is this qname an xmlns declaration?" [qn] @@ -146,6 +135,26 @@ (and (str/blank? uri) (= "xmlns" (qname-local qn)))))) +(defn xmlns-attr-prefix [qn] + (let [uri (qname-uri qn)] + (if (str/blank? uri) + (do (when-not (= "xmlns" (qname-local qn)) + (throw (ex-info "Not an xmlns-attr name" {:qname qn}))) + "") + (do (when-not (= xmlns-uri uri) + (throw (ex-info "Not an xmlns-attr name" {:qname qn}))) + (qname-local qn))))) + +(defn legal-xmlns-binding! [prefix uri] + (when (not= (= "xml" prefix) + (= xml-uri uri)) + (throw (ex-info (str "The xmlns binding for prefix `xml` is fixed to `" xml-uri "`") + {:attempted-mapping {:prefix prefix :uri uri}}))) + (when (not= (= "xmlns" prefix) + (= xmlns-uri uri)) + (throw (ex-info (str "The xmlns binding for prefix `xmlns` is fixed to `" xmlns-uri "`") + {:attempted-mapping {:prefix prefix :uri uri}})))) + (defn separate-xmlns "Call cont with two args: attributes and xmlns attributes" [attrs cont] @@ -155,9 +164,11 @@ (if (seq attrs') (let [val (get attrs qn)] (if (xmlns-attr? qn) - (recur attrs* - (assoc! xmlns* (qname-local qn) val) - (next attrs')) + (let [prefix (xmlns-attr-prefix qn)] + (legal-xmlns-binding! prefix val) + (recur attrs* + (assoc! xmlns* prefix val) + (next attrs'))) (recur (assoc! attrs* qn val) xmlns* (next attrs')))) diff --git a/src/main/clojure/clojure/data/xml/process.clj b/src/main/clojure/clojure/data/xml/process.clj index 16d12e3..747c6fe 100644 --- a/src/main/clojure/clojure/data/xml/process.clj +++ b/src/main/clojure/clojure/data/xml/process.clj @@ -3,7 +3,8 @@ [clojure.data.xml.name :as name :refer [gen-prefix *gen-prefix-counter* qname-uri]] [clojure.data.xml.node :refer [element] :as node] [clojure.data.xml.tree :refer [flatten-elements] :as tree] - [clojure.string :as str])) + [clojure.string :as str] + [clojure.data.xml.pu-map :as pu])) (defn- reduce-tree "Optimized reducer for in-order traversal of nodes, with reduce-like accumulator" @@ -26,7 +27,8 @@ (if (map? el) (reduce-kv (fn [s attr _] (xf s (qname-uri attr))) - (xf s (qname-uri (:tag el))) (:attrs el)) + (xf s (qname-uri (:tag el))) + (:attrs el)) s))) (defn find-xmlns @@ -42,11 +44,8 @@ (with-meta xml {:clojure.data.xml/nss (binding [*gen-prefix-counter* 0] - (persistent! - (reduce (fn [tm uri] - (if (str/blank? uri) - tm - (assoc! tm (keyword "xmlns" (gen-prefix)) uri))) - (transient {}) (find-xmlns xml))))})) - - + (-> (fn [tm uri] + (pu/assoc! tm (gen-prefix) uri)) + qname-uri-xf + (reduce-tree (pu/transient pu/EMPTY) xml) + pu/persistent!))})) diff --git a/src/main/clojure/clojure/data/xml/pu_map.cljc b/src/main/clojure/clojure/data/xml/pu_map.cljc index d6c21fe..c43ae06 100644 --- a/src/main/clojure/clojure/data/xml/pu_map.cljc +++ b/src/main/clojure/clojure/data/xml/pu_map.cljc @@ -12,10 +12,24 @@ [clojure.core :as core]) (:refer-clojure :exclude [assoc! dissoc! transient persistent! get assoc])) -(defn transient [{:keys [u->ps p->u]}] - (core/assoc! (core/transient {}) - :p->u (core/transient p->u) - :u->ps (core/transient u->ps))) +(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! @@ -35,27 +49,24 @@ (core/dissoc! u->ps uri))) (defn assoc! [{:as put :keys [p->u u->ps]} prefix uri] - (when (or (core/get #{"xml" "xmlns"} prefix) - (core/get #{name/xml-uri name/xmlns-uri} uri)) - (throw (ex-info "Mapping for xml: and xmlns: prefixes are fixed by the standard" - {:attempted-mapping {:prefix prefix - :uri uri}}))) - (let [prev-uri (core/get p->u prefix)] + (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)) + (core/dissoc! p->u prefix*) + (core/assoc! p->u prefix* uri)) :u->ps (if (str/blank? uri) - (dissoc-uri! u->ps prev-uri prefix) + (dissoc-uri! u->ps prev-uri prefix*) (cond (= uri prev-uri) u->ps - (not prev-uri) (assoc-uri! u->ps uri prefix) + (not prev-uri) (assoc-uri! u->ps uri prefix*) :else (-> u->ps - (dissoc-uri! prev-uri prefix) - (assoc-uri! uri prefix))))))) + (dissoc-uri! prev-uri prefix*) + (assoc-uri! uri prefix*))))))) (defn get [{:keys [p->u]} prefix] - (core/get p->u prefix)) + (core/get p->u (str prefix))) (defn get-prefixes [{:keys [u->ps]} uri] (core/get u->ps uri)) @@ -66,13 +77,6 @@ (persistent! (reduce-kv assoc! (transient put) kvs))) - -;; 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}}) - (defn reduce-diff "A high-performance diffing operation, that reduces f over changed and removed prefixes" [f s @@ -87,3 +91,13 @@ 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)) diff --git a/src/test/clojure/clojure/data/xml/test_emit.clj b/src/test/clojure/clojure/data/xml/test_emit.clj index 974dfcb..dfbb8d2 100644 --- a/src/test/clojure/clojure/data/xml/test_emit.clj +++ b/src/test/clojure/clojure/data/xml/test_emit.clj @@ -8,12 +8,14 @@ (ns ^{:doc "Tests for emit to print XML text." :author "Chris Houser"} - clojure.data.xml.test-emit + clojure.data.xml.test-emit (:require [clojure.test :refer :all] [clojure.data.xml :refer :all] [clojure.data.xml.test-utils :refer [test-stream lazy-parse*]] - [clojure.data.xml.impl :refer [compile-if]]) + [clojure.data.xml.impl :refer [compile-if]] + [clojure.data.xml.name :as name] + [clojure.data.xml.pu-map :as pu]) (:import (javax.xml.namespace QName))) (def deep-tree @@ -219,11 +221,23 @@ (element (as-qname "{NS2}bar")))] (is (= (parse-str (emit-str el)) el)))) +(alias-uri :xml name/xml-uri) + (deftest test-default-xmlns (let [nss-meta (comp :clojure.data.xml/nss meta)] - (is (= {"xmlns" "NS"} + (is (= (pu/merge-prefix-map nil {"" "NS"}) (nss-meta (parse-str "")) - (nss-meta (parse-str (emit-str (parse-str "")))))))) + (nss-meta (parse-str (emit-str (parse-str ""))))))) + (is (thrown? Exception (emit-str {:tag :el :attrs {(name/qname name/xmlns-uri "xml") "foo"}}))) + (is (thrown? Exception (emit-str {:tag :el :attrs {(name/qname name/xmlns-uri "xmlns") "foo"}}))) + (is (thrown? Exception (emit-str {:tag :el :attrs {:xmlns/xml "foo"}}))) + (is (thrown? Exception (emit-str {:tag :el :attrs {:xmlns/xmlns "foo"}}))) + (is (thrown? Exception (parse-str "")) + "TODO: find out if this is standard conforming, or a bug in StAX") + (is (= (emit-str {:tag :el :attrs {:xmlns/xmlns "http://www.w3.org/2000/xmlns/"}}) + "")) + (is (= (emit-str {:tag :el :attrs {:xmlns/xml "http://www.w3.org/XML/1998/namespace" ::xml/lang "en"}}) + ""))) (deftest test-empty-elements (is (= (emit-str {:tag :a :content []}) "")) @@ -233,5 +247,12 @@ (is (= (emit-str (with-meta (parse-str "") nil)) "")) - (is (= (emit-str (parse-str "")) - ""))) + (is (= (emit-str (parse-str "")) + "")) + (is (= (emit-str (parse-str "")) + "")) + ; builtins + (is (= (emit-str (parse-str "")) + "")) + (is (thrown? Exception (parse-str "")) + "TODO: find out if this is standard conforming, or a bug in StAX")) diff --git a/src/test/clojure/clojure/data/xml/test_process.cljc b/src/test/clojure/clojure/data/xml/test_process.cljc index e476e5d..278a8f4 100644 --- a/src/test/clojure/clojure/data/xml/test_process.cljc +++ b/src/test/clojure/clojure/data/xml/test_process.cljc @@ -4,13 +4,14 @@ find-xmlns])]] [clojure.test :refer [deftest is]] [clojure.walk :as w] - [clojure.string :as str])) + [clojure.string :as str] + [clojure.data.xml.pu-map :as pu])) (def test-data (element :foo nil (with-meta (element :bar {:xmlns "MOO:"} "some" "content") - {:clojure.data.xml/nss {:xmlns/p "PAR:"}}) + {:clojure.data.xml/nss (pu/merge-prefix-map nil {"p" "PAR:"})}) "more content" (element (qname "GOO:" "ho") {(qname "GEE:" "hi") "ma"} "ii") "end")) @@ -19,7 +20,8 @@ (:clj (deftest process (is (= (find-xmlns test-data) #{"" "GEE:" "GOO:"})) - (is (= (set (vals (element-nss (aggregate-xmlns test-data)))) #{"GEE:" "GOO:"})))) + (let [nss (set (vals (:p->u (element-nss (aggregate-xmlns test-data)))))] + (is (every? #(contains? nss %) ["GEE:" "GOO:"]))))) (deftest walk-test (is (= {:tag :FOO, :attrs {}, :content ()} diff --git a/src/test/clojure/clojure/data/xml/test_pu.cljc b/src/test/clojure/clojure/data/xml/test_pu.cljc index fe40989..9e58b3e 100644 --- a/src/test/clojure/clojure/data/xml/test_pu.cljc +++ b/src/test/clojure/clojure/data/xml/test_pu.cljc @@ -33,6 +33,17 @@ name/xml-uri ["xml"] name/xmlns-uri ["xmlns"]] + [[nil "FIN:"]] + ["wrong-prefix" nil + "xml" name/xml-uri + "xmlns" name/xmlns-uri + "" "FIN:" + nil "FIN:"] + ["wrong-uri" nil + "FIN:" [""] + name/xml-uri ["xml"] + name/xmlns-uri ["xmlns"]] + [["p" "U:" "q" "V:"]] ["wrong-prefix" nil @@ -57,7 +68,26 @@ "q" nil "r" "U:"] ["U:" ["r" "t"] - "V:" ["s"]])) + "V:" ["s"]] + + [["xml" name/xml-uri + "xmlns" name/xmlns-uri]] + ["xml" name/xml-uri + "xmlns" name/xmlns-uri] + [name/xml-uri ["xml"] + name/xmlns-uri ["xmlns"]])) + +(deftest assoc-nil + (let [pu (pu/assoc nil nil "NIL")] + (is (= "NIL" (pu/get pu nil) (pu/get pu ""))) + (is (= "" (pu/get-prefix pu "NIL"))))) + +(deftest direct-access + (is (= {"" "NIL" "a" "A" + "xml" name/xml-uri + "xmlns" name/xmlns-uri} + (pu/prefix-map + (pu/assoc nil "a" "A" nil "NIL"))))) (deftest diffing (is (= {"c" "d"} From 28ee2cdb08f1083e0f14345fcf7836a653cbb60a Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Tue, 24 Oct 2017 19:47:18 +0200 Subject: [PATCH 169/237] update clojurescript to play nice with clojure 1.9 --- pom.xml | 2 +- project.clj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 3163802..256bd41 100644 --- a/pom.xml +++ b/pom.xml @@ -51,7 +51,7 @@ org.clojure clojurescript - 1.9.908 + 1.9.946 test diff --git a/project.clj b/project.clj index a0245bc..031cc8f 100644 --- a/project.clj +++ b/project.clj @@ -4,7 +4,7 @@ :resource-paths ["src/test/resources" "target/gen-resources"] :dependencies [[org.clojure/clojure "1.8.0"] [org.clojure/data.codec "0.1.0"] - [org.clojure/clojurescript "1.9.562"] + [org.clojure/clojurescript "1.9.946"] [com.cemerick/piggieback "0.2.2"] [org.clojure/tools.nrepl "0.2.13"] [org.clojure/test.check "0.9.0"] From 0faad8a14b2ea7d14c1e43802a014171006fb43f Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Wed, 25 Oct 2017 11:43:26 +0200 Subject: [PATCH 170/237] fix test_emit for XML Parser change in java9 --- src/test/clojure/clojure/data/xml/test_emit.clj | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/test/clojure/clojure/data/xml/test_emit.clj b/src/test/clojure/clojure/data/xml/test_emit.clj index dfbb8d2..24563a9 100644 --- a/src/test/clojure/clojure/data/xml/test_emit.clj +++ b/src/test/clojure/clojure/data/xml/test_emit.clj @@ -153,9 +153,11 @@ (deftest test-indent-str-with-doctype (let [nested-xml (lazy-parse* (str "foo")) doctype "" - expect (str doctype "\n\n \n \n foo\n \n \n\n") - result (indent-str nested-xml :doctype doctype)] - (is (= expect (subs result (.indexOf result doctype)))))) + expect "\n\n \n \n foo\n \n \n\n" + result (indent-str nested-xml :doctype doctype) + offset-dt (.indexOf result "" offset-dt))] + (is (= expect (subs result offset-res))))) (defmacro are-serializable [group-description extra-attrs & {:as data-strings}] `(testing ~group-description From ef8d0c816fa8d9c02d759de990bdb2e0042c0d2a Mon Sep 17 00:00:00 2001 From: Michael Blume Date: Tue, 11 Jul 2017 15:31:56 -0700 Subject: [PATCH 171/237] DXML-46 Remove reflection from data.xml --- src/main/clojure/clojure/data/xml/impl.clj | 2 +- src/main/clojure/clojure/data/xml/jvm/parse.clj | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/main/clojure/clojure/data/xml/impl.clj b/src/main/clojure/clojure/data/xml/impl.clj index 77ee759..4595354 100644 --- a/src/main/clojure/clojure/data/xml/impl.clj +++ b/src/main/clojure/clojure/data/xml/impl.clj @@ -63,4 +63,4 @@ `(do ~else))) (defn b64-encode [ba] - (String. (b64/encode ba))) + (String. ^bytes (b64/encode ba))) diff --git a/src/main/clojure/clojure/data/xml/jvm/parse.clj b/src/main/clojure/clojure/data/xml/jvm/parse.clj index 0be4431..8b631bc 100644 --- a/src/main/clojure/clojure/data/xml/jvm/parse.clj +++ b/src/main/clojure/clojure/data/xml/jvm/parse.clj @@ -17,6 +17,7 @@ [qname]] [clojure.data.xml.pu-map :as pu]) (:import + (java.io InputStream Reader) (javax.xml.stream XMLInputFactory XMLStreamReader XMLStreamConstants) (clojure.data.xml.event EndElementEvent))) @@ -114,7 +115,7 @@ ;; Consume and ignore comments, spaces, processing instructions etc (recur)))))) -(defn- make-input-factory [props] +(defn- make-input-factory ^XMLInputFactory [props] (let [fac (XMLInputFactory/newInstance)] (doseq [[k v] props :when (contains? input-factory-props k) @@ -124,11 +125,11 @@ (defn make-stream-reader [props source] (let [fac (make-input-factory props)] - ;; Reflection on following line cannot be eliminated via a - ;; type hint, because s is advertised by fn parse to be an - ;; InputStream or Reader, and there are different - ;; createXMLStreamReader signatures for each of those types. - (.createXMLStreamReader fac source))) + (cond + (instance? Reader source) (.createXMLStreamReader fac ^Reader source) + (instance? InputStream source) (.createXMLStreamReader fac ^InputStream source) + :else (throw (IllegalArgumentException. + "source should be java.io.Reader or java.io.InputStream"))))) (defn string-source [s] (java.io.StringReader. s)) From 760e8c054c889ee1ca79a09bc79788c976f5d371 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Mon, 13 Nov 2017 23:35:11 +0100 Subject: [PATCH 172/237] :exclude redefined `merge from :refer-clojure in pu_map.cljc --- src/main/clojure/clojure/data/xml/pu_map.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/clojure/clojure/data/xml/pu_map.cljc b/src/main/clojure/clojure/data/xml/pu_map.cljc index c43ae06..d179cf2 100644 --- a/src/main/clojure/clojure/data/xml/pu_map.cljc +++ b/src/main/clojure/clojure/data/xml/pu_map.cljc @@ -10,7 +10,7 @@ (:require [clojure.data.xml.name :as name] [clojure.string :as str] [clojure.core :as core]) - (:refer-clojure :exclude [assoc! dissoc! transient persistent! get assoc])) + (:refer-clojure :exclude [assoc! dissoc! transient persistent! get assoc merge])) (def prefix-map :p->u) (def uri-map :u->ps) From b73e53bcf74ab688ca4fedf362f66dece2e2b431 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Mon, 13 Nov 2017 23:52:21 +0100 Subject: [PATCH 173/237] ignore cljs test failure on jdk >= 1.9, due to CLJS-2377 --- src/test/clojure/clojure/data/xml/test_cljs.clj | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/test/clojure/clojure/data/xml/test_cljs.clj b/src/test/clojure/clojure/data/xml/test_cljs.clj index 31de1e7..a837630 100644 --- a/src/test/clojure/clojure/data/xml/test_cljs.clj +++ b/src/test/clojure/clojure/data/xml/test_cljs.clj @@ -15,12 +15,17 @@ (try (require 'clojure.data.xml.cljs-testsuite) (eval '(clojure.data.xml.cljs-testsuite/run-testsuite! "target/cljs-test-nashorn")) + (when (not (neg? (compare (System/getProperty "java.runtime.version") + "1.9"))) + (println "CELEBRATION: CLJS-2377 has been fixed. Hooray! Please add jdk >= 1.9 back to the test matrix")) (catch Exception e (if (or (neg? (compare ((juxt :major :minor) *clojure-version*) [1 8])) (neg? (compare (System/getProperty "java.runtime.version") - "1.8"))) - (println "WARN: ignoring cljs testsuite error on clojure < 1.8 or jdk < 1.8" + "1.8")) + (not (neg? (compare (System/getProperty "java.runtime.version") + "1.9")))) + (println "WARN: ignoring cljs testsuite error on clojure < 1.8 or jdk < 1.8 also on jdk >= 1.9, see CLJS-2377" *clojure-version* (System/getProperty "java.runtime.name") (System/getProperty "java.vm.version") (System/getProperty "java.runtime.version") \newline (str e)) From e4e521e3aab45ad9ea0a6ad44e358daee5cd21c7 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Tue, 14 Nov 2017 00:01:13 +0100 Subject: [PATCH 174/237] Prepare docs for 0.2.0-alpha4 --- CHANGES.md | 4 ++++ README.md | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index f4234d6..91e36f2 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,7 @@ +From 0.2.0-alpha3 to 0.2.0-alpha4 +- Fix error check for builtin prefixes DXML-49 +- Remove reflection cases DXML-46 + From 0.2.0-alpha2 to 0.2.0-alpha3 - Minimum requirement is now clojure 1.7.0 - Print newline after preamble when pretty-printing (DXML-35) diff --git a/README.md b/README.md index c5a0df8..0b78d10 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Please report bugs using JIRA [here](http://dev.clojure.org/jira/browse/DXML). Latest stable release: `0.0.8` -Latest preview release: `0.2.0-alpha3` +Latest preview release: `0.2.0-alpha4` (The main features of the `0.2.0` series are XML Namespace support and Clojurescript support) @@ -49,7 +49,7 @@ For Maven projects, add the following XML in your `pom.xml`'s `` s org.clojure data.xml - 0.2.0-alpha3 + 0.2.0-alpha4 ### Leiningen @@ -63,7 +63,7 @@ Add the following to the `project.clj` dependencies: For preview: - [org.clojure/data.xml "0.2.0-alpha3"] + [org.clojure/data.xml "0.2.0-alpha4"] ## Examples From ff59f431fb2be03c6a939d6da6b668eedc4ea7b7 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Tue, 14 Nov 2017 00:30:55 +0100 Subject: [PATCH 175/237] Prepare docs for 0.2.0-alpha5 due to my mishandling hudson (pushing while it builds), we need to skip -alpha4. sorry. --- CHANGES.md | 2 +- README.md | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 91e36f2..875d32f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,4 +1,4 @@ -From 0.2.0-alpha3 to 0.2.0-alpha4 +From 0.2.0-alpha3 to 0.2.0-alpha5 - Fix error check for builtin prefixes DXML-49 - Remove reflection cases DXML-46 diff --git a/README.md b/README.md index 0b78d10..ddb3f40 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Please report bugs using JIRA [here](http://dev.clojure.org/jira/browse/DXML). Latest stable release: `0.0.8` -Latest preview release: `0.2.0-alpha4` +Latest preview release: `0.2.0-alpha5` (The main features of the `0.2.0` series are XML Namespace support and Clojurescript support) @@ -49,7 +49,7 @@ For Maven projects, add the following XML in your `pom.xml`'s `` s org.clojure data.xml - 0.2.0-alpha4 + 0.2.0-alpha5 ### Leiningen @@ -63,7 +63,7 @@ Add the following to the `project.clj` dependencies: For preview: - [org.clojure/data.xml "0.2.0-alpha4"] + [org.clojure/data.xml "0.2.0-alpha5"] ## Examples From a92905c47d0405534af4264d5c6ba8e4039592b6 Mon Sep 17 00:00:00 2001 From: "Hudson @ build.clojure.org" Date: Mon, 13 Nov 2017 17:32:47 -0600 Subject: [PATCH 176/237] [maven-release-plugin] prepare release data.xml-0.2.0-alpha5 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 256bd41..77720a7 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ 4.0.0 data.xml - 0.2.0-SNAPSHOT + 0.2.0-alpha5 data.xml jar Functions to parse XML into lazy sequences and lazy trees and emit these as text @@ -85,7 +85,7 @@ scm:git:git@github.com:clojure/data.xml.git scm:git:git@github.com:clojure/data.xml.git git@github.com:clojure/data.xml.git - HEAD + data.xml-0.2.0-alpha5 From 41366e182519a763700b7c58ec46379062c01b78 Mon Sep 17 00:00:00 2001 From: "Hudson @ build.clojure.org" Date: Mon, 13 Nov 2017 17:32:47 -0600 Subject: [PATCH 177/237] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 77720a7..256bd41 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ 4.0.0 data.xml - 0.2.0-alpha5 + 0.2.0-SNAPSHOT data.xml jar Functions to parse XML into lazy sequences and lazy trees and emit these as text @@ -85,7 +85,7 @@ scm:git:git@github.com:clojure/data.xml.git scm:git:git@github.com:clojure/data.xml.git git@github.com:clojure/data.xml.git - data.xml-0.2.0-alpha5 + HEAD From e03f0994783af5bc24d822f480ad1c0c9fb876df Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Tue, 6 Nov 2018 02:54:24 +0100 Subject: [PATCH 178/237] test against clojurescript 1.10.439 this makes DXML-59 visible to the test suite --- pom.xml | 2 +- project.clj | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 256bd41..60e3e4e 100644 --- a/pom.xml +++ b/pom.xml @@ -51,7 +51,7 @@ org.clojure clojurescript - 1.9.946 + 1.10.439 test diff --git a/project.clj b/project.clj index 031cc8f..3a9cb30 100644 --- a/project.clj +++ b/project.clj @@ -2,9 +2,9 @@ :source-paths ["src/main/clojure"] :test-paths ["src/test/clojure" "src/test/clojurescript"] :resource-paths ["src/test/resources" "target/gen-resources"] - :dependencies [[org.clojure/clojure "1.8.0"] + :dependencies [[org.clojure/clojure "1.10.0-RC1"] [org.clojure/data.codec "0.1.0"] - [org.clojure/clojurescript "1.9.946"] + [org.clojure/clojurescript "1.10.439"] [com.cemerick/piggieback "0.2.2"] [org.clojure/tools.nrepl "0.2.13"] [org.clojure/test.check "0.9.0"] From 023314ab7ef9fe13b456ddd89dc0322ba5e1a194 Mon Sep 17 00:00:00 2001 From: Dimitri Fedorov Date: Thu, 2 Aug 2018 21:06:02 +0300 Subject: [PATCH 179/237] DXML-59: Typo in throw in clojure.data.xml.js.dom --- src/main/clojure/clojure/data/xml.cljs | 2 +- src/main/clojure/clojure/data/xml/js/dom.cljs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/clojure/data/xml.cljs b/src/main/clojure/clojure/data/xml.cljs index e54f463..7e31cb7 100644 --- a/src/main/clojure/clojure/data/xml.cljs +++ b/src/main/clojure/clojure/data/xml.cljs @@ -26,7 +26,7 @@ ;; see http://stackoverflow.com/questions/11563554/how-do-i-detect-xml-parsing-errors-when-using-javascripts-domparser-in-a-cross [s & {:keys [content-type on-error raw] :or {content-type "text/xml" - on-error #(throw "XML parser error" {:doc % :input s})}}] + on-error #(throw (ex-info "XML parser error" {:doc % :input s}))}}] (let [dom (. (js/DOMParser.) (parseFromString s content-type)) doc (.-documentElement dom)] diff --git a/src/main/clojure/clojure/data/xml/js/dom.cljs b/src/main/clojure/clojure/data/xml/js/dom.cljs index e080424..5db8b90 100644 --- a/src/main/clojure/clojure/data/xml/js/dom.cljs +++ b/src/main/clojure/clojure/data/xml/js/dom.cljs @@ -173,7 +173,7 @@ :tag (dom-element-tag el) :attrs (.-attributes el) :content (.-childNodes el) - (throw "XML tag has no key" {:key k :el el}))) + (throw (ex-info "XML tag has no key" {:key k :el el})))) ([el k nf] #_(println "Element" k "=>" (case k :tag (dom-element-tag el) From 0720752ea5fcddc0cdfa4ccd5eb33682b4d9530c Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Tue, 6 Nov 2018 03:11:46 +0100 Subject: [PATCH 180/237] DXML-61: check basic tag printing in cljs --- .../clojurescript/clojure/data/xml/test_cljs_basic.cljs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/test/clojurescript/clojure/data/xml/test_cljs_basic.cljs b/src/test/clojurescript/clojure/data/xml/test_cljs_basic.cljs index fdec66e..6fc217b 100644 --- a/src/test/clojurescript/clojure/data/xml/test_cljs_basic.cljs +++ b/src/test/clojurescript/clojure/data/xml/test_cljs_basic.cljs @@ -19,3 +19,10 @@ (is (= dxml (xml/parse-str (xml/emit-str dxml))))) (xml/element :foo) "" (xml/element :xmlns.DAV%3A/foo) "")) + +(deftest printing + (are [node ps] (is (= ps (pr-str node))) + (xml/element :foo) "#xml/element{:tag :foo}" + (xml/element :foo {:a "2"}) "#xml/element{:tag :foo, :attrs {:a \"2\"}}" + (xml/element :foo {} (xml/element :bar)) "#xml/element{:tag :foo, :content [#xml/element{:tag :bar}]}" + (xml/element :foo {} "bar") "#xml/element{:tag :foo, :content [\"bar\"]}")) From 7947b16c69983080e4f62dec27111ff1a26ed538 Mon Sep 17 00:00:00 2001 From: Dimitri Fedorov Date: Tue, 6 Nov 2018 03:14:56 +0100 Subject: [PATCH 181/237] DXML-61: avoid calling -pr-writer directly in cljs element printer --- src/main/clojure/clojure/data/xml/node.cljc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/clojure/data/xml/node.cljc b/src/main/clojure/clojure/data/xml/node.cljc index 201c062..808bceb 100644 --- a/src/main/clojure/clojure/data/xml/node.cljc +++ b/src/main/clojure/clojure/data/xml/node.cljc @@ -151,13 +151,13 @@ [IPrintWithWriter (-pr-writer [this writer opts] (-write writer "#xml/element{:tag ") - (-pr-writer tag writer opts) + (pr-writer tag writer opts) (when-not (empty? attrs) (-write writer ", :attrs ") - (-pr-writer attrs writer opts)) + (pr-writer attrs writer opts)) (when-not (empty? content) (-write writer ", :content ") - (pr-sequential-writer writer -pr-writer "[" " " "]" opts content)) + (pr-sequential-writer writer pr-writer "[" " " "]" opts content)) (-write writer "}"))])) ;; clj printing is a multimethod From 60e6cdf988c6ec8a28d4aee807f79bc065d7135a Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Tue, 6 Nov 2018 17:14:33 +0100 Subject: [PATCH 182/237] DXML-47 DXML-54 implement cdata and comment nodes for cljs --- src/main/clojure/clojure/data/xml/js/dom.cljs | 10 ++++++++++ .../clojure/data/xml/test_cljs_basic.cljs | 4 +++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/clojure/clojure/data/xml/js/dom.cljs b/src/main/clojure/clojure/data/xml/js/dom.cljs index 5db8b90..a8816fc 100644 --- a/src/main/clojure/clojure/data/xml/js/dom.cljs +++ b/src/main/clojure/clojure/data/xml/js/dom.cljs @@ -79,6 +79,8 @@ (def NamedNodeMap (type (.-attributes (element :e)))) (def NodeList (type (node-list []))) (def Attr (type (aget (.-attributes (element :e {:a "1"})) 0))) +(def CData (type (cdata ""))) +(def Comment (type (xml-comment ""))) ;; ## Coercions @@ -89,7 +91,11 @@ [el] (cond (string? el) (text-node el) + (instance? node/CData el) (cdata (:content el)) + (instance? node/Comment el) (xml-comment (:content el)) (instance? Element el) el + (instance? CData el) el + (instance? Comment el) el ;; stupid xmldom, (some? (.-item el)) #_(instance? NodeList el) (some? (.-item el)) el @@ -141,6 +147,10 @@ "Coerce xml elements to element maps / content vectors" [el] (cond + (instance? Comment el) + (node/xml-comment (.-data el)) + (instance? CData el) + (node/cdata (.-data el)) (instance? Text el) (.-nodeValue el) (instance? Element el) diff --git a/src/test/clojurescript/clojure/data/xml/test_cljs_basic.cljs b/src/test/clojurescript/clojure/data/xml/test_cljs_basic.cljs index 6fc217b..dadab4a 100644 --- a/src/test/clojurescript/clojure/data/xml/test_cljs_basic.cljs +++ b/src/test/clojurescript/clojure/data/xml/test_cljs_basic.cljs @@ -18,7 +18,9 @@ (are [dxml xml] (do (is (= dxml (xml/parse-str xml))) (is (= dxml (xml/parse-str (xml/emit-str dxml))))) (xml/element :foo) "" - (xml/element :xmlns.DAV%3A/foo) "")) + (xml/element :xmlns.DAV%3A/foo) "" + (xml/element :foo {} (xml/cdata "" + (xml/element :foo {} (xml/xml-comment " bar> ")) "")) (deftest printing (are [node ps] (is (= ps (pr-str node))) From f05bcf4eedbe813945ee9005b53cb1b761aa5989 Mon Sep 17 00:00:00 2001 From: "Hudson @ build.clojure.org" Date: Sat, 24 Nov 2018 09:09:12 -0600 Subject: [PATCH 183/237] [maven-release-plugin] prepare release data.xml-0.2.0-alpha6 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 60e3e4e..0e1c82a 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ 4.0.0 data.xml - 0.2.0-SNAPSHOT + 0.2.0-alpha6 data.xml jar Functions to parse XML into lazy sequences and lazy trees and emit these as text @@ -85,7 +85,7 @@ scm:git:git@github.com:clojure/data.xml.git scm:git:git@github.com:clojure/data.xml.git git@github.com:clojure/data.xml.git - HEAD + data.xml-0.2.0-alpha6 From 243d8627f72fe7440ce6da42b206d9ad56780f04 Mon Sep 17 00:00:00 2001 From: "Hudson @ build.clojure.org" Date: Sat, 24 Nov 2018 09:09:12 -0600 Subject: [PATCH 184/237] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 0e1c82a..60e3e4e 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ 4.0.0 data.xml - 0.2.0-alpha6 + 0.2.0-SNAPSHOT data.xml jar Functions to parse XML into lazy sequences and lazy trees and emit these as text @@ -85,7 +85,7 @@ scm:git:git@github.com:clojure/data.xml.git scm:git:git@github.com:clojure/data.xml.git git@github.com:clojure/data.xml.git - data.xml-0.2.0-alpha6 + HEAD From 04db68059e1f4ba3b07ee153cb1a73175ede51cd Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Sat, 24 Nov 2018 16:19:10 +0100 Subject: [PATCH 185/237] Update README and changelog --- CHANGES.md | 3 +++ README.md | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 875d32f..170f3f7 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,6 @@ +From 0.2.0-alpha5 to 0.2.0-alpha6 +- ClojureScript implementation fixes and tests + From 0.2.0-alpha3 to 0.2.0-alpha5 - Fix error check for builtin prefixes DXML-49 - Remove reflection cases DXML-46 diff --git a/README.md b/README.md index ddb3f40..9e52f4a 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Please report bugs using JIRA [here](http://dev.clojure.org/jira/browse/DXML). Latest stable release: `0.0.8` -Latest preview release: `0.2.0-alpha5` +Latest preview release: `0.2.0-alpha6` (The main features of the `0.2.0` series are XML Namespace support and Clojurescript support) @@ -49,7 +49,7 @@ For Maven projects, add the following XML in your `pom.xml`'s `` s org.clojure data.xml - 0.2.0-alpha5 + 0.2.0-alpha6 ### Leiningen @@ -63,7 +63,7 @@ Add the following to the `project.clj` dependencies: For preview: - [org.clojure/data.xml "0.2.0-alpha5"] + [org.clojure/data.xml "0.2.0-alpha6"] ## Examples From ad3b9fd8549e328ae991f32250973a8bd035159f Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Sat, 24 Nov 2018 21:01:24 +0100 Subject: [PATCH 186/237] Revert "ignore cljs test failure on jdk >= 1.9, due to CLJS-2377" This reverts commit b73e53bcf74ab688ca4fedf362f66dece2e2b431. --- src/test/clojure/clojure/data/xml/test_cljs.clj | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/test/clojure/clojure/data/xml/test_cljs.clj b/src/test/clojure/clojure/data/xml/test_cljs.clj index a837630..31de1e7 100644 --- a/src/test/clojure/clojure/data/xml/test_cljs.clj +++ b/src/test/clojure/clojure/data/xml/test_cljs.clj @@ -15,17 +15,12 @@ (try (require 'clojure.data.xml.cljs-testsuite) (eval '(clojure.data.xml.cljs-testsuite/run-testsuite! "target/cljs-test-nashorn")) - (when (not (neg? (compare (System/getProperty "java.runtime.version") - "1.9"))) - (println "CELEBRATION: CLJS-2377 has been fixed. Hooray! Please add jdk >= 1.9 back to the test matrix")) (catch Exception e (if (or (neg? (compare ((juxt :major :minor) *clojure-version*) [1 8])) (neg? (compare (System/getProperty "java.runtime.version") - "1.8")) - (not (neg? (compare (System/getProperty "java.runtime.version") - "1.9")))) - (println "WARN: ignoring cljs testsuite error on clojure < 1.8 or jdk < 1.8 also on jdk >= 1.9, see CLJS-2377" + "1.8"))) + (println "WARN: ignoring cljs testsuite error on clojure < 1.8 or jdk < 1.8" *clojure-version* (System/getProperty "java.runtime.name") (System/getProperty "java.vm.version") (System/getProperty "java.runtime.version") \newline (str e)) From a0bd59e69f2d26d73ed1c9b7e1185922ab888f93 Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Sun, 25 Nov 2018 15:34:22 +0100 Subject: [PATCH 187/237] dep update --- pom.xml | 2 +- project.clj | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pom.xml b/pom.xml index 60e3e4e..1821612 100644 --- a/pom.xml +++ b/pom.xml @@ -39,7 +39,7 @@ org.clojure data.codec - 0.1.0 + 0.1.1 compile diff --git a/project.clj b/project.clj index 3a9cb30..6b1b3ee 100644 --- a/project.clj +++ b/project.clj @@ -1,13 +1,13 @@ (defproject org.clojure/data.xml "0-UE-DEVELOPMENT" - :source-paths ["src/main/clojure"] + :source-paths ["src/main/clojure" "src/main/clojurescript"] :test-paths ["src/test/clojure" "src/test/clojurescript"] - :resource-paths ["src/test/resources" "target/gen-resources"] - :dependencies [[org.clojure/clojure "1.10.0-RC1"] - [org.clojure/data.codec "0.1.0"] + :resource-paths ["src/main/resources" "src/test/resources" "target/gen-resources"] + :dependencies [[org.clojure/clojure "1.10.0-beta8"] + [org.clojure/data.codec "0.1.1"] [org.clojure/clojurescript "1.10.439"] [com.cemerick/piggieback "0.2.2"] [org.clojure/tools.nrepl "0.2.13"] [org.clojure/test.check "0.9.0"] - [figwheel-sidecar "0.5.10"] - [binaryage/devtools "0.9.4"]] + [figwheel-sidecar "0.5.17"] + [binaryage/devtools "0.9.10"]] :repl-options {:nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]}) From ad559c2c8e830d22796936c6043862081f02fbaf Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Sun, 28 Apr 2019 15:33:04 -0700 Subject: [PATCH 188/237] Update links in CONTRIBUTING.md --- CONTRIBUTING.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8c5d243..f785f72 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,12 +3,10 @@ This is a [Clojure contrib] project. Under the Clojure contrib [guidelines], this project cannot accept pull requests. All patches must be submitted via [JIRA]. -See [Contributing] and the [FAQ] on the Clojure development [wiki] for +See [Contributing] on the Clojure website for more information on how to contribute. -[Clojure contrib]: http://dev.clojure.org/display/doc/Clojure+Contrib -[Contributing]: http://dev.clojure.org/display/community/Contributing -[FAQ]: http://dev.clojure.org/display/community/Contributing+FAQ +[Clojure contrib]: https://clojure.org/community/contrib_libs +[Contributing]: https://clojure.org/community/contributing [JIRA]: http://dev.clojure.org/jira/browse/DXML -[guidelines]: http://dev.clojure.org/display/community/Guidelines+for+Clojure+Contrib+committers -[wiki]: http://dev.clojure.org/ +[guidelines]: https://clojure.org/community/contrib_howto From 63386cf3f66e491ff3896913423d961011dd3e3f Mon Sep 17 00:00:00 2001 From: Herwig Hochleitner Date: Mon, 13 May 2019 23:02:19 +0200 Subject: [PATCH 189/237] DXML-62: add :xml/... shorthand namespace --- src/main/clojure/clojure/data/xml/name.cljc | 5 +++-- src/test/clojure/clojure/data/xml/test_emit.clj | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/clojure/data/xml/name.cljc b/src/main/clojure/clojure/data/xml/name.cljc index 76edbe9..99b8866 100644 --- a/src/main/clojure/clojure/data/xml/name.cljc +++ b/src/main/clojure/clojure/data/xml/name.cljc @@ -74,8 +74,9 @@ (if-let [ns (namespace kw)] (if (.startsWith ns "xmlns.") (decode-uri (subs ns 6)) - (if (= "xmlns" ns) - xmlns-uri + (case ns + "xmlns" xmlns-uri + "xml" xml-uri (throw (ex-info "Keyword ns is not an xmlns. Needs to be in the form :xmlns./" {:kw kw})))) ""))) diff --git a/src/test/clojure/clojure/data/xml/test_emit.clj b/src/test/clojure/clojure/data/xml/test_emit.clj index 24563a9..b350208 100644 --- a/src/test/clojure/clojure/data/xml/test_emit.clj +++ b/src/test/clojure/clojure/data/xml/test_emit.clj @@ -239,6 +239,8 @@ (is (= (emit-str {:tag :el :attrs {:xmlns/xmlns "http://www.w3.org/2000/xmlns/"}}) "")) (is (= (emit-str {:tag :el :attrs {:xmlns/xml "http://www.w3.org/XML/1998/namespace" ::xml/lang "en"}}) + "")) + (is (= (emit-str {:tag :el :attrs {:xml/lang "en"}}) ""))) (deftest test-empty-elements From e4a50a68c80acf7cd6e48c0438b4bd08acaff331 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Fri, 20 Dec 2019 08:07:46 -0600 Subject: [PATCH 190/237] Replace data-codec use for base64 encoding with Base64 support built into the JDK as of Java 8 --- pom.xml | 6 ------ project.clj | 1 - src/main/clojure/clojure/data/xml/impl.clj | 9 ++++++--- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/pom.xml b/pom.xml index 1821612..ea052fc 100644 --- a/pom.xml +++ b/pom.xml @@ -36,12 +36,6 @@ - - org.clojure - data.codec - 0.1.1 - compile - org.clojure test.check diff --git a/project.clj b/project.clj index 6b1b3ee..eb91d2b 100644 --- a/project.clj +++ b/project.clj @@ -3,7 +3,6 @@ :test-paths ["src/test/clojure" "src/test/clojurescript"] :resource-paths ["src/main/resources" "src/test/resources" "target/gen-resources"] :dependencies [[org.clojure/clojure "1.10.0-beta8"] - [org.clojure/data.codec "0.1.1"] [org.clojure/clojurescript "1.10.439"] [com.cemerick/piggieback "0.2.2"] [org.clojure/tools.nrepl "0.2.13"] diff --git a/src/main/clojure/clojure/data/xml/impl.clj b/src/main/clojure/clojure/data/xml/impl.clj index 4595354..c6435a6 100644 --- a/src/main/clojure/clojure/data/xml/impl.clj +++ b/src/main/clojure/clojure/data/xml/impl.clj @@ -9,7 +9,9 @@ (ns clojure.data.xml.impl "Shared private code for data.xml namespaces" {:author "Herwig Hochleitner"} - (:require [clojure.data.codec.base64 :as b64])) + (:import + [java.util Base64] + [java.nio.charset StandardCharsets])) (defn- var-form? [form] (and (seq? form) (= 'var (first form)))) @@ -62,5 +64,6 @@ `(do ~then) `(do ~else))) -(defn b64-encode [ba] - (String. ^bytes (b64/encode ba))) +(defn b64-encode [^bytes ba] + (let [encoder (Base64/getEncoder)] + (String. (.encode encoder ba) StandardCharsets/ISO_8859_1))) \ No newline at end of file From 6fa4830bddcb4de212dad3bb642713a17d61ad99 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Fri, 20 Dec 2019 08:08:47 -0600 Subject: [PATCH 191/237] update changelog --- CHANGES.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 170f3f7..02d268c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,6 @@ +From 0.2.0-alpha6 to ??? +- Replace data.codec with using Base64, now in the JDK + From 0.2.0-alpha5 to 0.2.0-alpha6 - ClojureScript implementation fixes and tests From 564bec24c4bb6dc70f7e45ba76b60bc2d35ff09d Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Thu, 18 Jun 2020 13:05:06 -0500 Subject: [PATCH 192/237] add LICENSE text file --- LICENSE | 205 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e246f6a --- /dev/null +++ b/LICENSE @@ -0,0 +1,205 @@ +Eclipse Public License - v 1.0 + +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC +LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM +CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + +a) in the case of the initial Contributor, the initial code and documentation + distributed under this Agreement, and +b) in the case of each subsequent Contributor: + i) changes to the Program, and + ii) additions to the Program; + + where such changes and/or additions to the Program originate from and are + distributed by that particular Contributor. A Contribution 'originates' + from a Contributor if it was added to the Program by such Contributor + itself or anyone acting on such Contributor's behalf. Contributions do not + include additions to the Program which: (i) are separate modules of + software distributed in conjunction with the Program under their own + license agreement, and (ii) are not derivative works of the Program. + +"Contributor" means any person or entity that distributes the Program. + +"Licensed Patents" mean patent claims licensable by a Contributor which are +necessarily infringed by the use or sale of its Contribution alone or when +combined with the Program. + +"Program" means the Contributions distributed in accordance with this +Agreement. + +"Recipient" means anyone who receives the Program under this Agreement, +including all Contributors. + +2. GRANT OF RIGHTS + a) Subject to the terms of this Agreement, each Contributor hereby grants + Recipient a non-exclusive, worldwide, royalty-free copyright license to + reproduce, prepare derivative works of, publicly display, publicly + perform, distribute and sublicense the Contribution of such Contributor, + if any, and such derivative works, in source code and object code form. + b) Subject to the terms of this Agreement, each Contributor hereby grants + Recipient a non-exclusive, worldwide, royalty-free patent license under + Licensed Patents to make, use, sell, offer to sell, import and otherwise + transfer the Contribution of such Contributor, if any, in source code and + object code form. This patent license shall apply to the combination of + the Contribution and the Program if, at the time the Contribution is + added by the Contributor, such addition of the Contribution causes such + combination to be covered by the Licensed Patents. The patent license + shall not apply to any other combinations which include the Contribution. + No hardware per se is licensed hereunder. + c) Recipient understands that although each Contributor grants the licenses + to its Contributions set forth herein, no assurances are provided by any + Contributor that the Program does not infringe the patent or other + intellectual property rights of any other entity. Each Contributor + disclaims any liability to Recipient for claims brought by any other + entity based on infringement of intellectual property rights or + otherwise. As a condition to exercising the rights and licenses granted + hereunder, each Recipient hereby assumes sole responsibility to secure + any other intellectual property rights needed, if any. For example, if a + third party patent license is required to allow Recipient to distribute + the Program, it is Recipient's responsibility to acquire that license + before distributing the Program. + d) Each Contributor represents that to its knowledge it has sufficient + copyright rights in its Contribution, if any, to grant the copyright + license set forth in this Agreement. + +3. REQUIREMENTS + +A Contributor may choose to distribute the Program in object code form under +its own license agreement, provided that: + + a) it complies with the terms and conditions of this Agreement; and + b) its license agreement: + i) effectively disclaims on behalf of all Contributors all warranties + and conditions, express and implied, including warranties or + conditions of title and non-infringement, and implied warranties or + conditions of merchantability and fitness for a particular purpose; + ii) effectively excludes on behalf of all Contributors all liability for + damages, including direct, indirect, special, incidental and + consequential damages, such as lost profits; + iii) states that any provisions which differ from this Agreement are + offered by that Contributor alone and not by any other party; and + iv) states that source code for the Program is available from such + Contributor, and informs licensees how to obtain it in a reasonable + manner on or through a medium customarily used for software exchange. + +When the Program is made available in source code form: + + a) it must be made available under this Agreement; and + b) a copy of this Agreement must be included with each copy of the Program. + Contributors may not remove or alter any copyright notices contained + within the Program. + +Each Contributor must identify itself as the originator of its Contribution, +if +any, in a manner that reasonably allows subsequent Recipients to identify the +originator of the Contribution. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain responsibilities with +respect to end users, business partners and the like. While this license is +intended to facilitate the commercial use of the Program, the Contributor who +includes the Program in a commercial product offering should do so in a manner +which does not create potential liability for other Contributors. Therefore, +if a Contributor includes the Program in a commercial product offering, such +Contributor ("Commercial Contributor") hereby agrees to defend and indemnify +every other Contributor ("Indemnified Contributor") against any losses, +damages and costs (collectively "Losses") arising from claims, lawsuits and +other legal actions brought by a third party against the Indemnified +Contributor to the extent caused by the acts or omissions of such Commercial +Contributor in connection with its distribution of the Program in a commercial +product offering. The obligations in this section do not apply to any claims +or Losses relating to any actual or alleged intellectual property +infringement. In order to qualify, an Indemnified Contributor must: +a) promptly notify the Commercial Contributor in writing of such claim, and +b) allow the Commercial Contributor to control, and cooperate with the +Commercial Contributor in, the defense and any related settlement +negotiations. The Indemnified Contributor may participate in any such claim at +its own expense. + +For example, a Contributor might include the Program in a commercial product +offering, Product X. That Contributor is then a Commercial Contributor. If +that Commercial Contributor then makes performance claims, or offers +warranties related to Product X, those performance claims and warranties are +such Commercial Contributor's responsibility alone. Under this section, the +Commercial Contributor would have to defend claims against the other +Contributors related to those performance claims and warranties, and if a +court requires any other Contributor to pay any damages as a result, the +Commercial Contributor must pay those damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR +IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, +NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each +Recipient is solely responsible for determining the appropriateness of using +and distributing the Program and assumes all risks associated with its +exercise of rights under this Agreement , including but not limited to the +risks and costs of program errors, compliance with applicable laws, damage to +or loss of data, programs or equipment, and unavailability or interruption of +operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY +CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION +LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE +EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGES. + +7. GENERAL + +If any provision of this Agreement is invalid or unenforceable under +applicable law, it shall not affect the validity or enforceability of the +remainder of the terms of this Agreement, and without further action by the +parties hereto, such provision shall be reformed to the minimum extent +necessary to make such provision valid and enforceable. + +If Recipient institutes patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Program itself +(excluding combinations of the Program with other software or hardware) +infringes such Recipient's patent(s), then such Recipient's rights granted +under Section 2(b) shall terminate as of the date such litigation is filed. + +All Recipient's rights under this Agreement shall terminate if it fails to +comply with any of the material terms or conditions of this Agreement and does +not cure such failure in a reasonable period of time after becoming aware of +such noncompliance. If all Recipient's rights under this Agreement terminate, +Recipient agrees to cease use and distribution of the Program as soon as +reasonably practicable. However, Recipient's obligations under this Agreement +and any licenses granted by Recipient relating to the Program shall continue +and survive. + +Everyone is permitted to copy and distribute copies of this Agreement, but in +order to avoid inconsistency the Agreement is copyrighted and may only be +modified in the following manner. The Agreement Steward reserves the right to +publish new versions (including revisions) of this Agreement from time to +time. No one other than the Agreement Steward has the right to modify this +Agreement. The Eclipse Foundation is the initial Agreement Steward. The +Eclipse Foundation may assign the responsibility to serve as the Agreement +Steward to a suitable separate entity. Each new version of the Agreement will +be given a distinguishing version number. The Program (including +Contributions) may always be distributed subject to the version of the +Agreement under which it was received. In addition, after a new version of the +Agreement is published, Contributor may elect to distribute the Program +(including its Contributions) under the new version. Except as expressly +stated in Sections 2(a) and 2(b) above, Recipient receives no rights or +licenses to the intellectual property of any Contributor under this Agreement, +whether expressly, by implication, estoppel or otherwise. All rights in the +Program not expressly granted under this Agreement are reserved. + +This Agreement is governed by the laws of the State of New York and the +intellectual property laws of the United States of America. No party to this +Agreement will bring a legal action under this Agreement more than one year +after the cause of action arose. Each party waives its rights to a jury trial in +any resulting litigation. + + From cb557e2fddd36dbd8eb099da8ece145196937a6e Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Tue, 1 Sep 2020 13:41:44 -0700 Subject: [PATCH 193/237] Add CLI/deps.edn dependency information --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 9e52f4a..f948115 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,17 @@ Add the following to the `project.clj` dependencies: [org.clojure/data.xml "0.2.0-alpha6"] +### [CLI/`deps.edn`](https://clojure.org/reference/deps_and_cli) + +Add the following to the `deps.edn` dependencies: +```clojure +;; for stable version: +org.clojure/data.xml {:mvn/version "0.0.8"} + +;; for preview version: +org.clojure/data.xml {:mvn/version "0.2.0-alpha6"} +``` + ## Examples The examples below assume you have added a `:refer :all` for data.xml: From 0a26f131658c58d16a20175ca0bba40330ce33c0 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Wed, 16 Sep 2020 12:40:12 -0500 Subject: [PATCH 194/237] add copyright statement to code --- src/main/clojure/clojure/data/xml.cljs | 8 ++++++++ src/main/clojure/clojure/data/xml/js/dom.cljs | 8 ++++++++ src/main/clojure/clojure/data/xml/js/name.cljs | 8 ++++++++ src/main/clojure/clojure/data/xml/process.clj | 8 ++++++++ src/main/clojure/clojure/data/xml/pu_map.cljc | 8 ++++++++ 5 files changed, 40 insertions(+) diff --git a/src/main/clojure/clojure/data/xml.cljs b/src/main/clojure/clojure/data/xml.cljs index 7e31cb7..8c64e0b 100644 --- a/src/main/clojure/clojure/data/xml.cljs +++ b/src/main/clojure/clojure/data/xml.cljs @@ -1,3 +1,11 @@ +; 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 (:require-macros [clojure.data.xml.impl :refer [export-api]]) diff --git a/src/main/clojure/clojure/data/xml/js/dom.cljs b/src/main/clojure/clojure/data/xml/js/dom.cljs index a8816fc..c945386 100644 --- a/src/main/clojure/clojure/data/xml/js/dom.cljs +++ b/src/main/clojure/clojure/data/xml/js/dom.cljs @@ -1,3 +1,11 @@ +; 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.js.dom (:require [clojure.data.xml.name :refer [qname-uri qname-local qname xmlns-uri]] diff --git a/src/main/clojure/clojure/data/xml/js/name.cljs b/src/main/clojure/clojure/data/xml/js/name.cljs index 22c85bc..e4e8eab 100644 --- a/src/main/clojure/clojure/data/xml/js/name.cljs +++ b/src/main/clojure/clojure/data/xml/js/name.cljs @@ -1,3 +1,11 @@ +; 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.js.name (:require [clojure.data.xml.protocols :refer [AsQName qname-uri qname-local]] [clojure.string :as str])) diff --git a/src/main/clojure/clojure/data/xml/process.clj b/src/main/clojure/clojure/data/xml/process.clj index 747c6fe..457e948 100644 --- a/src/main/clojure/clojure/data/xml/process.clj +++ b/src/main/clojure/clojure/data/xml/process.clj @@ -1,3 +1,11 @@ +; 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.process (:require [clojure.data.xml.event :refer [element-nss] :as evt] [clojure.data.xml.name :as name :refer [gen-prefix *gen-prefix-counter* qname-uri]] diff --git a/src/main/clojure/clojure/data/xml/pu_map.cljc b/src/main/clojure/clojure/data/xml/pu_map.cljc index d179cf2..a14a0fb 100644 --- a/src/main/clojure/clojure/data/xml/pu_map.cljc +++ b/src/main/clojure/clojure/data/xml/pu_map.cljc @@ -1,3 +1,11 @@ +; 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. From d4484b9e0a1a61ce075f4dad6d11fb6a073fea4f Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Thu, 22 Oct 2020 10:59:40 -0500 Subject: [PATCH 195/237] Update docstring for event-seq to show default option values --- src/main/clojure/clojure/data/xml.clj | 31 ++++++++++++++------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/main/clojure/clojure/data/xml.clj b/src/main/clojure/clojure/data/xml.clj index 9552130..c455d7d 100644 --- a/src/main/clojure/clojure/data/xml.clj +++ b/src/main/clojure/clojure/data/xml.clj @@ -55,21 +55,22 @@ Input source can be a java.io.InputStream or java.io.Reader Options: - :include-node? can be a subset of #{:element :characters :comment} default #{:element :characters} - :location-info pass false to skip generating location meta data - -See http://docs.oracle.com/javase/6/docs/api/javax/xml/stream/XMLInputFactory.html -for documentation on options: - - {:allocator XMLInputFactory/ALLOCATOR - :coalescing XMLInputFactory/IS_COALESCING - :namespace-aware XMLInputFactory/IS_NAMESPACE_AWARE - :replacing-entity-references XMLInputFactory/IS_REPLACING_ENTITY_REFERENCES - :supporting-external-entities XMLInputFactory/IS_SUPPORTING_EXTERNAL_ENTITIES - :validating XMLInputFactory/IS_VALIDATING - :reporter XMLInputFactory/REPORTER - :resolver XMLInputFactory/RESOLVER - :support-dtd XMLInputFactory/SUPPORT_DTD}" + :include-node? subset of #{:element :characters :comment}, default #{:element :characters} + :location-info pass false to skip generating location meta data, default true + +See https://docs.oracle.com/javase/8/docs/api/javax/xml/stream/XMLInputFactory.html +for documentation on xml options. These are the defaults: + + {:allocator nil ; XMLInputFactory/ALLOCATOR + :coalescing true ; XMLInputFactory/IS_COALESCING + :namespace-aware true ; XMLInputFactory/IS_NAMESPACE_AWARE + :replacing-entity-references true ; XMLInputFactory/IS_REPLACING_ENTITY_REFERENCES + :supporting-external-entities false ; XMLInputFactory/IS_SUPPORTING_EXTERNAL_ENTITIES + :validating false ; XMLInputFactory/IS_VALIDATING + :reporter nil ; XMLInputFactory/REPORTER + :resolver nil ; XMLInputFactory/RESOLVER + :support-dtd true ; XMLInputFactory/SUPPORT_DTD + }" {:arglists (list ['source parser-opts-arg])} [source opts] (let [props* (merge {:include-node? #{:element :characters} From b8b4bfbb85ec9cc54ce45ef8e2b91d1958a51621 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Thu, 22 Oct 2020 11:01:49 -0500 Subject: [PATCH 196/237] Extend doc-changes from event-seq to parse and parse-str --- src/main/clojure/clojure/data/xml.clj | 62 ++++++++++++++------------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/src/main/clojure/clojure/data/xml.clj b/src/main/clojure/clojure/data/xml.clj index c455d7d..39279c4 100644 --- a/src/main/clojure/clojure/data/xml.clj +++ b/src/main/clojure/clojure/data/xml.clj @@ -90,21 +90,22 @@ Input source can be a java.io.InputStream or java.io.Reader Options: - :include-node? can be a subset of #{:element :characters :comment} default #{:element :characters} - :location-info pass false to skip generating location meta data - -See http://docs.oracle.com/javase/6/docs/api/javax/xml/stream/XMLInputFactory.html -for documentation on options: - - {:allocator XMLInputFactory/ALLOCATOR - :coalescing XMLInputFactory/IS_COALESCING - :namespace-aware XMLInputFactory/IS_NAMESPACE_AWARE - :replacing-entity-references XMLInputFactory/IS_REPLACING_ENTITY_REFERENCES - :supporting-external-entities XMLInputFactory/IS_SUPPORTING_EXTERNAL_ENTITIES - :validating XMLInputFactory/IS_VALIDATING - :reporter XMLInputFactory/REPORTER - :resolver XMLInputFactory/RESOLVER - :support-dtd XMLInputFactory/SUPPORT_DTD}" + :include-node? subset of #{:element :characters :comment}, default #{:element :characters} + :location-info pass false to skip generating location meta data, default true + +See https://docs.oracle.com/javase/8/docs/api/javax/xml/stream/XMLInputFactory.html +for documentation on xml options. These are the defaults: + + {:allocator nil ; XMLInputFactory/ALLOCATOR + :coalescing true ; XMLInputFactory/IS_COALESCING + :namespace-aware true ; XMLInputFactory/IS_NAMESPACE_AWARE + :replacing-entity-references true ; XMLInputFactory/IS_REPLACING_ENTITY_REFERENCES + :supporting-external-entities false ; XMLInputFactory/IS_SUPPORTING_EXTERNAL_ENTITIES + :validating false ; XMLInputFactory/IS_VALIDATING + :reporter nil ; XMLInputFactory/REPORTER + :resolver nil ; XMLInputFactory/RESOLVER + :support-dtd true ; XMLInputFactory/SUPPORT_DTD + }" {:arglists (list ['source '& parser-opts-arg])} [source & {:as opts}] (event-tree (event-seq source opts))) @@ -114,21 +115,22 @@ for documentation on options: Options: - :include-node? can be a subset of #{:element :characters :comment} default #{:element :characters} - :location-info pass false to skip generating location meta data - -See http://docs.oracle.com/javase/6/docs/api/javax/xml/stream/XMLInputFactory.html -for documentation on options: - - {:allocator XMLInputFactory/ALLOCATOR - :coalescing XMLInputFactory/IS_COALESCING - :namespace-aware XMLInputFactory/IS_NAMESPACE_AWARE - :replacing-entity-references XMLInputFactory/IS_REPLACING_ENTITY_REFERENCES - :supporting-external-entities XMLInputFactory/IS_SUPPORTING_EXTERNAL_ENTITIES - :validating XMLInputFactory/IS_VALIDATING - :reporter XMLInputFactory/REPORTER - :resolver XMLInputFactory/RESOLVER - :support-dtd XMLInputFactory/SUPPORT_DTD}" + :include-node? subset of #{:element :characters :comment}, default #{:element :characters} + :location-info pass false to skip generating location meta data, default true + +See https://docs.oracle.com/javase/8/docs/api/javax/xml/stream/XMLInputFactory.html +for documentation on xml options. These are the defaults: + + {:allocator nil ; XMLInputFactory/ALLOCATOR + :coalescing true ; XMLInputFactory/IS_COALESCING + :namespace-aware true ; XMLInputFactory/IS_NAMESPACE_AWARE + :replacing-entity-references true ; XMLInputFactory/IS_REPLACING_ENTITY_REFERENCES + :supporting-external-entities false ; XMLInputFactory/IS_SUPPORTING_EXTERNAL_ENTITIES + :validating false ; XMLInputFactory/IS_VALIDATING + :reporter nil ; XMLInputFactory/REPORTER + :resolver nil ; XMLInputFactory/RESOLVER + :support-dtd true ; XMLInputFactory/SUPPORT_DTD + }" {:arglists (list ['string '& parser-opts-arg])} [s & opts] (apply parse (string-source s) opts)) From 97c715acb339ac3e20269620b3fccc480bbd9722 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Thu, 22 Oct 2020 11:18:55 -0500 Subject: [PATCH 197/237] add to gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index e3436a0..e060776 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ modules/xml.pull-parser/target/ /figwheel_server.log /.nrepl-port /.cljs_nashorn_repl/ +.idea/ +*.iml From 029b7739df4d80d5c4404809bfe5163b55537fb7 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Wed, 20 Jan 2021 14:43:28 -0600 Subject: [PATCH 198/237] update to latest parent pom --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ea052fc..c83d52c 100644 --- a/pom.xml +++ b/pom.xml @@ -32,7 +32,7 @@ org.clojure pom.contrib - 0.2.2 + 1.0.0 From 12cc9934607de6cb4d75eddb1fcae30829fa4156 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Thu, 18 Feb 2021 08:53:58 -0600 Subject: [PATCH 199/237] update old links --- CONTRIBUTING.md | 2 +- README.md | 25 +++++++++------------ src/main/clojure/clojure/data/xml/node.cljc | 4 ++-- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f785f72..5e9af93 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,5 +8,5 @@ more information on how to contribute. [Clojure contrib]: https://clojure.org/community/contrib_libs [Contributing]: https://clojure.org/community/contributing -[JIRA]: http://dev.clojure.org/jira/browse/DXML +[JIRA]: https://jira.atlassian.net/browse/DXML [guidelines]: https://clojure.org/community/contrib_howto diff --git a/README.md b/README.md index f948115..9414581 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [data.xml](https://github.com/clojure/data.xml) is a Clojure library for reading and writing XML data. This library is the successor to -[lazy-xml](http://clojure.github.com/clojure-contrib/lazy-xml-api.html). +[lazy-xml](https://clojure.github.io/clojure-contrib/lazy-xml-api.html). data.xml has the following features: * Parses XML documents into Clojure data structures @@ -13,11 +13,11 @@ data.xml has the following features: ## API Reference -Generated API docs for data.xml are available [here](http://clojure.github.com/data.xml). +Generated API docs for data.xml are available [here](https://clojure.github.io/data.xml). ## Bugs -Please report bugs using JIRA [here](http://dev.clojure.org/jira/browse/DXML). +Please report bugs using JIRA [here](https://clojure.atlassian.net/browse/DXML). ## Installation @@ -27,7 +27,7 @@ Latest preview release: `0.2.0-alpha6` (The main features of the `0.2.0` series are XML Namespace support and Clojurescript support) -* [All Released Versions](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22data.xml%22) +* [All Released Versions](https://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.clojure%22%20AND%20a%3A%22data.xml%22) * [Development Snapshot Versions](https://oss.sonatype.org/index.html#nexus-search;gav~org.clojure~data.xml~~~) @@ -84,9 +84,9 @@ The examples below assume you have added a `:refer :all` for data.xml: data.xml supports parsing and emitting XML. The parsing functions will read XML from a -[Reader](http://docs.oracle.com/javase/6/docs/api/java/io/Reader.html) +[Reader](https://docs.oracle.com/javase/8/docs/api/java/io/Reader.html) or -[InputStream](http://docs.oracle.com/javase/6/docs/api/java/io/InputStream.html). +[InputStream](https://docs.oracle.com/javase/8/docs/api/java/io/InputStream.html). (let [input-xml (java.io.StringReader. " The baz value")] @@ -109,7 +109,7 @@ can be passed via key pairs: XML elements can be created using the typical defrecord constructor functions or the element function used below or just a plain map with :tag :attrs :content keys, and written using a -[java.io.Writer](http://docs.oracle.com/javase/6/docs/api/java/io/Writer.html).: +[java.io.Writer](https://docs.oracle.com/javase/8/docs/api/java/io/Writer.html).: (let [tags (element :foo {:foo-attr "foo value"} (element :bar {:bar-attr "bar value"} @@ -331,7 +331,7 @@ Some utilities, like `process/*-xmlns`, `prxml/sexp-as-*`, `indent` aren't yet i Make `extend-dom-as-data!` also support assoc, ... on dom nodes. -#### Feel free to pick a [ticket](http://dev.clojure.org/jira/secure/IssueNavigator.jspa?reset=true&jqlQuery=project+%3D+DXML+AND+status+in+%28Open%2C+%22In+Progress%22%2C+Reopened%29) to work on +#### Feel free to pick a [ticket](https://clojure.atlassian.net/browse/DXML) to work on ## License @@ -340,16 +340,13 @@ Licensed under the [Eclipse Public License](http://www.opensource.org/licenses/e ## Developer Information * [GitHub project](https://github.com/clojure/data.xml) - -* [Bug Tracker](http://dev.clojure.org/jira/browse/DXML) - +* [Bug Tracker](https://clojure.atlassian.net/browse/DXML) * [Continuous Integration](http://build.clojure.org/job/data.xml/) - * [Compatibility Test Matrix](http://build.clojure.org/job/data.xml-test-matrix/) ## Contributing All contributions need to be made via patches attached to tickets in -[JIRA](http://dev.clojure.org/jira/browse/DXML). Check the -[Contributing to Clojure](http://clojure.org/contributing) page for +[JIRA](http://clojure.atlassian.net/browse/DXML). Check the +[Contributing to Clojure](https://clojure.org/community/contributing) page for more information. diff --git a/src/main/clojure/clojure/data/xml/node.cljc b/src/main/clojure/clojure/data/xml/node.cljc index 808bceb..4a31454 100644 --- a/src/main/clojure/clojure/data/xml/node.cljc +++ b/src/main/clojure/clojure/data/xml/node.cljc @@ -23,11 +23,11 @@ ;; it is similar to (defrecord Element [tag attrs content]) ;; but we override its hash and equality to be compatible with ;; clojure's hash-maps -;; see http://dev.clojure.org/jira/browse/CLJ-2084 +;; see https://clojure.atlassian.net/browse/CLJ-2084 ;; also, elements don't have an extmap and degrade to hash-maps also ;; when assoc'ing unknown keys -;; FIXME hash caching cannot be used: http://dev.clojure.org/jira/browse/CLJ-2092 +;; FIXME hash caching cannot be used: https://clojure.atlassian.net/browse/CLJ-2092 #? (:clj From da84d4f3a9151f94a3846d752c9ec1fb8e195764 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Thu, 15 Apr 2021 15:56:47 -0500 Subject: [PATCH 200/237] update parent pom version to latest --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c83d52c..2f5b1de 100644 --- a/pom.xml +++ b/pom.xml @@ -32,7 +32,7 @@ org.clojure pom.contrib - 1.0.0 + 1.1.0 From 4c6f193168792ea1090077f34c1c84990ccb795c Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Wed, 1 Sep 2021 09:23:48 -0500 Subject: [PATCH 201/237] remove assert that is not asserting anything --- src/main/clojure/clojure/data/xml/name.cljc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/clojure/clojure/data/xml/name.cljc b/src/main/clojure/clojure/data/xml/name.cljc index 99b8866..834c9fd 100644 --- a/src/main/clojure/clojure/data/xml/name.cljc +++ b/src/main/clojure/clojure/data/xml/name.cljc @@ -121,7 +121,6 @@ [& ans] (loop [[a n & rst :as ans] ans] (when (seq ans) - (assert (<= (count ans)) (pr-str ans)) (let [xn (uri-symbol n) al (symbol (clj-ns-name a))] (create-ns xn) From 11f882460a2f8441d4c08748635e24d56ea04e18 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sun, 12 Dec 2021 15:35:15 -0600 Subject: [PATCH 202/237] update to latest test.check --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2f5b1de..b005f13 100644 --- a/pom.xml +++ b/pom.xml @@ -39,7 +39,7 @@ org.clojure test.check - 0.9.0 + 1.1.1 test From b8694061bbf0970f95fa5a0178fe28e23f3eea82 Mon Sep 17 00:00:00 2001 From: ikappaki Date: Tue, 8 Feb 2022 20:46:36 +0000 Subject: [PATCH 203/237] Add copy of cljs.repl.nashorn r.1.0.439 https://raw.githubusercontent.com/clojure/clojurescript/r1.10.439/src/main/clojure/cljs/repl/nashorn.clj --- .../clojure/data/xml/cljs_repl_nashorn.clj | 193 ++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 src/test/resources/clojure/data/xml/cljs_repl_nashorn.clj diff --git a/src/test/resources/clojure/data/xml/cljs_repl_nashorn.clj b/src/test/resources/clojure/data/xml/cljs_repl_nashorn.clj new file mode 100644 index 0000000..2991804 --- /dev/null +++ b/src/test/resources/clojure/data/xml/cljs_repl_nashorn.clj @@ -0,0 +1,193 @@ +;; Copyright (c) Rich Hickey. 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 cljs.repl.nashorn + (:require [clojure.java.io :as io] + [clojure.string :as string] + [clojure.stacktrace] + [clojure.data.json :as json] + [cljs.analyzer :as ana] + [cljs.env :as env] + [cljs.util :as util] + [cljs.repl :as repl] + [cljs.cli :as cli] + [cljs.compiler :as comp] + [cljs.closure :as closure] + [cljs.stacktrace :as st]) + (:import [javax.script ScriptEngine ScriptEngineManager ScriptException ScriptEngineFactory])) + +(util/compile-if (Class/forName "jdk.nashorn.api.scripting.NashornException") + (do + (import 'jdk.nashorn.api.scripting.NashornException) + ;; Implementation + + (defn create-engine + ([] (create-engine nil)) + ([{:keys [code-cache] :or {code-cache true}}] + (let [args (when code-cache ["-pcc"]) + factories (.getEngineFactories (ScriptEngineManager.)) + factory (get (zipmap (map #(.getEngineName %) factories) factories) "Oracle Nashorn")] + (if-let [engine (if-not (empty? args) + (.getScriptEngine ^ScriptEngineFactory factory (into-array args)) + (.getScriptEngine ^ScriptEngineFactory factory))] + (let [context (.getContext engine)] + (.setWriter context *out*) + (.setErrorWriter context *err*) + engine) + (throw (IllegalArgumentException. + "Cannot find the Nashorn script engine, use a JDK version 8 or higher.")))))) + + (defn eval-str [^ScriptEngine engine ^String s] + (.eval engine s)) + + (defn eval-resource + "Evaluate a file on the classpath in the engine." + [engine path debug] + (let [r (io/resource path)] + (eval-str engine (slurp r)) + (when debug (println "loaded: " path)))) + + (defn init-engine [engine {:keys [output-dir] :as opts} debug] + (eval-str engine (format "var CLJS_DEBUG = %s;" (boolean debug))) + (eval-str engine (format "var CLJS_OUTPUT_DIR = \"%s\";" output-dir)) + (eval-resource engine "goog/base.js" debug) + (eval-resource engine "goog/deps.js" debug) + (eval-resource engine "cljs/bootstrap_nashorn.js" debug) + (eval-str engine + (format "goog.global.CLOSURE_UNCOMPILED_DEFINES = %s;" + (json/write-str (:closure-defines opts)))) + engine) + + (defn tear-down-engine [engine] + (eval-str engine "nashorn_tear_down();")) + + (defn load-js-file [engine file] + (eval-str engine (format "nashorn_load(\"%s\");" file))) + + ;; Create a minimal build of Clojurescript from the core library. + ;; Copied from clj.cljs.repl.node. + (defn bootstrap-repl [engine output-dir opts] + (env/ensure + (let [deps-file ".nashorn_repl_deps.js" + core (io/resource "cljs/core.cljs") + core-js (closure/compile core + (assoc opts :output-file + (closure/src-file->target-file + core (dissoc opts :output-dir)))) + deps (closure/add-dependencies opts core-js)] + ;; output unoptimized code and the deps file + ;; for all compiled namespaces + (apply closure/output-unoptimized + (assoc opts :output-to (.getPath (io/file output-dir deps-file))) + deps) + ;; load the deps file so we can goog.require cljs.core etc. + (load-js-file engine deps-file)))) + + (defn load-ns [engine ns] + (eval-str engine + (format "goog.require(\"%s\");" (comp/munge (first ns))))) + + ;; Nashorn script stacktraces have a relative path which includes the output-dir + (defn- strip-file-name [^String file-name output-dir] + (let [with-slash (str output-dir "/")] + (if (.startsWith file-name with-slash) + (string/replace-first file-name with-slash "") + file-name))) + + (def repl-filename "") + + (defrecord NashornEnv [engine debug] + repl/IReplEnvOptions + (-repl-options [this] + {:output-dir ".cljs_nashorn_repl" + :target :nashorn}) + repl/IJavaScriptEnv + (-setup [this {:keys [output-dir bootstrap output-to] :as opts}] + (init-engine engine opts debug) + (let [env (ana/empty-env)] + (if output-to + (load-js-file engine output-to) + (bootstrap-repl engine output-dir opts)) + (repl/evaluate-form this env repl-filename + '(.require js/goog "cljs.core")) + ;; monkey-patch goog.isProvided_ to suppress useless errors + (repl/evaluate-form this env repl-filename + '(set! js/goog.isProvided_ (fn [ns] false))) + ;; monkey-patch goog.require to be more sensible + (repl/evaluate-form this env repl-filename + '(do + (set! *loaded-libs* #{"cljs.core"}) + (set! (.-require js/goog) + (fn [name reload] + (when (or (not (contains? *loaded-libs* name)) reload) + (set! *loaded-libs* (conj (or *loaded-libs* #{}) name)) + (js/CLOSURE_IMPORT_SCRIPT + (if (some? goog/debugLoader_) + (.getPathFromDeps_ goog/debugLoader_ name) + (goog.object/get (.. js/goog -dependencies_ -nameToPath) name)))))))))) + (-evaluate [{engine :engine :as this} filename line js] + (when debug (println "Evaluating: " js)) + (try + {:status :success + :value (if-let [r (eval-str engine js)] (.toString r) "")} + (catch ScriptException e + (let [^Throwable root-cause (clojure.stacktrace/root-cause e)] + {:status :exception + :value (.getMessage root-cause) + :stacktrace (NashornException/getScriptStackString root-cause)})) + (catch Throwable e + (let [^Throwable root-cause (clojure.stacktrace/root-cause e)] + {:status :exception + :value (.getMessage root-cause) + :stacktrace + (apply str + (interpose "\n" + (map str + (.getStackTrace root-cause))))})))) + (-load [{engine :engine :as this} ns url] + (load-ns engine ns)) + (-tear-down [this] + (tear-down-engine engine)) + repl/IParseStacktrace + (-parse-stacktrace [this frames-str ret opts] + (st/parse-stacktrace this frames-str + (assoc ret :ua-product :nashorn) opts)) + repl/IParseError + (-parse-error [_ err _] + (update-in err [:stacktrace] + (fn [st] + (string/join "\n" (drop 1 (string/split st #"\n"))))))) + + (defn repl-env* [{:keys [debug] :as opts}] + (let [engine (create-engine opts)] + (merge + (NashornEnv. engine debug) + opts))) + + (defn repl-env + "Create a Nashorn repl-env for use with the repl/repl* method in Clojurescript." + [& {:as opts}] + (repl-env* opts)) + + ;; ------------------------------------------------------------------------- + ;; Command Line Support + + (defn -main [& args] + (apply cli/main repl-env args))) + + (do + (defn repl-env* [{:keys [debug] :as opts}] + (throw (ex-info "Nashorn not supported" {:type :repl-error}))) + + (defn repl-env + "Create a Nashorn repl-env for use with the repl/repl* method in Clojurescript." + [& {:as opts}] + (throw (ex-info "Nashorn not available under this Java runtime" {:type :repl-error}))) + + (defn -main [] + (throw (ex-info "Nashorn not available under this Java runtime" {:type :repl-error}))))) From 86fe09126ccf15a8d393241d8c2a902564dc7189 Mon Sep 17 00:00:00 2001 From: ikappaki Date: Tue, 8 Feb 2022 21:37:19 +0000 Subject: [PATCH 204/237] DXML-67 support cljs tests on JDK [15,17] - Also fixes newline test errors on MS-Windows. --- pom.xml | 19 ++++++++++- project.clj | 7 ++-- .../clojure/clojure/data/xml/test_emit.clj | 10 ++++-- .../clojure/clojure/data/xml/test_pprint.clj | 4 ++- .../clojure/data/xml/cljs_repls.clj | 4 +-- .../clojure/data/xml/cljs_repl_nashorn.clj | 34 +++++++++++-------- .../clojure/data/xml/cljs_testsuite.clj | 2 +- 7 files changed, 54 insertions(+), 26 deletions(-) diff --git a/pom.xml b/pom.xml index b005f13..fa56cd5 100644 --- a/pom.xml +++ b/pom.xml @@ -35,6 +35,23 @@ 1.1.0 + + + openjdk-nashorn + + [15,18) + + + + org.openjdk.nashorn + nashorn-core + 15.3 + test + + + + + org.clojure @@ -64,7 +81,7 @@ - 1.7.0 + 1.8.0 diff --git a/project.clj b/project.clj index eb91d2b..517544b 100644 --- a/project.clj +++ b/project.clj @@ -4,9 +4,10 @@ :resource-paths ["src/main/resources" "src/test/resources" "target/gen-resources"] :dependencies [[org.clojure/clojure "1.10.0-beta8"] [org.clojure/clojurescript "1.10.439"] - [com.cemerick/piggieback "0.2.2"] + [cider/piggieback "0.5.3"] [org.clojure/tools.nrepl "0.2.13"] [org.clojure/test.check "0.9.0"] [figwheel-sidecar "0.5.17"] - [binaryage/devtools "0.9.10"]] - :repl-options {:nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]}) + [binaryage/devtools "0.9.10"] + [org.openjdk.nashorn/nashorn-core "15.3"]] + :repl-options {:nrepl-middleware [cider.piggieback/wrap-cljs-repl]}) diff --git a/src/test/clojure/clojure/data/xml/test_emit.clj b/src/test/clojure/clojure/data/xml/test_emit.clj index b350208..fa7fa5c 100644 --- a/src/test/clojure/clojure/data/xml/test_emit.clj +++ b/src/test/clojure/clojure/data/xml/test_emit.clj @@ -10,6 +10,7 @@ :author "Chris Houser"} clojure.data.xml.test-emit (:require + [clojure.string :as str] [clojure.test :refer :all] [clojure.data.xml :refer :all] [clojure.data.xml.test-utils :refer [test-stream lazy-parse*]] @@ -137,7 +138,8 @@ (deftest test-indent (let [nested-xml (lazy-parse* (str "foo")) - expect (str "\n \n \n foo\n \n \n\n") + expect (-> "\n \n \n foo\n \n \n\n" + (str/replace #"\n" (System/lineSeparator))) sw (java.io.StringWriter.) _ (indent nested-xml sw) result (.toString sw)] @@ -146,14 +148,16 @@ (deftest test-indent-str (let [nested-xml (lazy-parse* (str "foo")) - expect (str "\n \n \n foo\n \n \n\n") + expect (-> "\n \n \n foo\n \n \n\n" + (str/replace #"\n" (System/lineSeparator))) result (indent-str nested-xml)] (is (= expect (subs result (.indexOf result "")))))) (deftest test-indent-str-with-doctype (let [nested-xml (lazy-parse* (str "foo")) doctype "" - expect "\n\n \n \n foo\n \n \n\n" + expect (-> "\n\n \n \n foo\n \n \n\n" + (str/replace #"\n" (System/lineSeparator))) result (indent-str nested-xml :doctype doctype) offset-dt (.indexOf result "" offset-dt))] diff --git a/src/test/clojure/clojure/data/xml/test_pprint.clj b/src/test/clojure/clojure/data/xml/test_pprint.clj index d9693ca..989394f 100644 --- a/src/test/clojure/clojure/data/xml/test_pprint.clj +++ b/src/test/clojure/clojure/data/xml/test_pprint.clj @@ -10,6 +10,7 @@ :author "Herwig Hochleitner"} clojure.data.xml.test-pprint (:require + [clojure.string :as str] [clojure.test :refer :all] [clojure.data.xml :refer :all])) @@ -26,5 +27,6 @@ ")) (deftest test-indent - (is (= indented-xml (indent-str (parse-str xml))))) + (is (= (str/replace indented-xml #"\n" (System/lineSeparator)) + (indent-str (parse-str xml))))) diff --git a/src/test/clojurescript/clojure/data/xml/cljs_repls.clj b/src/test/clojurescript/clojure/data/xml/cljs_repls.clj index 4a329ff..be14beb 100644 --- a/src/test/clojurescript/clojure/data/xml/cljs_repls.clj +++ b/src/test/clojurescript/clojure/data/xml/cljs_repls.clj @@ -1,8 +1,8 @@ (ns clojure.data.xml.cljs-repls (:require [cljs.repl :as repl] - [cljs.repl.nashorn :as repl-nh] - [cemerick.piggieback :as pback] + [clojure.data.xml.cljs-repl-nashorn :as repl-nh] + [cider.piggieback :as pback] [cljs.closure :as closure] [figwheel-sidecar.repl-api :refer [start-figwheel! stop-figwheel! cljs-repl]])) diff --git a/src/test/resources/clojure/data/xml/cljs_repl_nashorn.clj b/src/test/resources/clojure/data/xml/cljs_repl_nashorn.clj index 2991804..eb8d41d 100644 --- a/src/test/resources/clojure/data/xml/cljs_repl_nashorn.clj +++ b/src/test/resources/clojure/data/xml/cljs_repl_nashorn.clj @@ -6,7 +6,13 @@ ;; the terms of this license. ;; You must not remove this notice, or any other, from this software. -(ns cljs.repl.nashorn +;; cljs repl nashorn update to support the openjdk nashorn package in +;; JDK versions greater than 15. +;; +;; Adapted from +;; https://raw.githubusercontent.com/clojure/clojurescript/r1.10.439/src/main/clojure/cljs/repl/nashorn.clj. + +(ns clojure.data.xml.cljs-repl-nashorn (:require [clojure.java.io :as io] [clojure.string :as string] [clojure.stacktrace] @@ -21,9 +27,17 @@ [cljs.stacktrace :as st]) (:import [javax.script ScriptEngine ScriptEngineManager ScriptException ScriptEngineFactory])) -(util/compile-if (Class/forName "jdk.nashorn.api.scripting.NashornException") +(def engine-name + (util/compile-if (Class/forName "org.openjdk.nashorn.api.scripting.NashornException") + (do + (import 'org.openjdk.nashorn.api.scripting.NashornException) + "OpenJDK Nashorn") + (do + (import 'jdk.nashorn.api.scripting.NashornException) + "Oracle Nashorn"))) + +(do (do - (import 'jdk.nashorn.api.scripting.NashornException) ;; Implementation (defn create-engine @@ -31,7 +45,7 @@ ([{:keys [code-cache] :or {code-cache true}}] (let [args (when code-cache ["-pcc"]) factories (.getEngineFactories (ScriptEngineManager.)) - factory (get (zipmap (map #(.getEngineName %) factories) factories) "Oracle Nashorn")] + factory (get (zipmap (map #(.getEngineName %) factories) factories) engine-name)] (if-let [engine (if-not (empty? args) (.getScriptEngine ^ScriptEngineFactory factory (into-array args)) (.getScriptEngine ^ScriptEngineFactory factory))] @@ -180,14 +194,4 @@ (defn -main [& args] (apply cli/main repl-env args))) - (do - (defn repl-env* [{:keys [debug] :as opts}] - (throw (ex-info "Nashorn not supported" {:type :repl-error}))) - - (defn repl-env - "Create a Nashorn repl-env for use with the repl/repl* method in Clojurescript." - [& {:as opts}] - (throw (ex-info "Nashorn not available under this Java runtime" {:type :repl-error}))) - - (defn -main [] - (throw (ex-info "Nashorn not available under this Java runtime" {:type :repl-error}))))) +) diff --git a/src/test/resources/clojure/data/xml/cljs_testsuite.clj b/src/test/resources/clojure/data/xml/cljs_testsuite.clj index 7820d1d..75df6ea 100644 --- a/src/test/resources/clojure/data/xml/cljs_testsuite.clj +++ b/src/test/resources/clojure/data/xml/cljs_testsuite.clj @@ -2,7 +2,7 @@ (:require [clojure.test :refer :all] [cljs.repl :as repl] - [cljs.repl.nashorn :as repl-nh] + [clojure.data.xml.cljs-repl-nashorn :as repl-nh] [cljs.closure :as closure] [cljs.build.api :as bapi] [clojure.string :as str] From 5e2071d216094f5ef544aac22cfcc0600acf7b45 Mon Sep 17 00:00:00 2001 From: Fogus Date: Fri, 5 Aug 2022 11:49:53 -0400 Subject: [PATCH 205/237] Adding a flag to toggle namespace awareness --- src/main/clojure/clojure/data/xml/jvm/parse.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/clojure/data/xml/jvm/parse.clj b/src/main/clojure/clojure/data/xml/jvm/parse.clj index 8b631bc..1b88ffd 100644 --- a/src/main/clojure/clojure/data/xml/jvm/parse.clj +++ b/src/main/clojure/clojure/data/xml/jvm/parse.clj @@ -69,7 +69,7 @@ (defn pull-seq "Creates a seq of events. The XMLStreamConstants/SPACE clause below doesn't seem to be triggered by the JDK StAX parser, but is by others. Leaving in to be more complete." - [^XMLStreamReader sreader {:keys [include-node? location-info skip-whitespace] :as opts} ns-envs] + [^XMLStreamReader sreader {:keys [include-node? location-info skip-whitespace namespace-aware] :as opts} ns-envs] (lazy-seq (loop [] (let [location (when location-info @@ -79,7 +79,7 @@ XMLStreamConstants/START_ELEMENT (if (include-node? :element) (let [ns-env (nss-hash sreader (or (first ns-envs) pu/EMPTY)) - tag (qname (.getNamespaceURI sreader) + tag (qname (when-not namespace-aware (.getNamespaceURI sreader)) (.getLocalName sreader) (.getPrefix sreader)) attrs (attr-hash sreader) From 7b577c1ac2d2b696a2f45cdd6891c47732a7132b Mon Sep 17 00:00:00 2001 From: Clojure Build Date: Fri, 5 Aug 2022 12:12:58 -0500 Subject: [PATCH 206/237] [maven-release-plugin] prepare release v0.2.0-alpha7 --- pom.xml | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/pom.xml b/pom.xml index fa56cd5..de07836 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ 4.0.0 data.xml - 0.2.0-SNAPSHOT + 0.2.0-alpha7 data.xml jar Functions to parse XML into lazy sequences and lazy trees and emit these as text @@ -35,23 +35,23 @@ 1.1.0 - - - openjdk-nashorn - - [15,18) - - - - org.openjdk.nashorn - nashorn-core - 15.3 - test - - - - - + + + openjdk-nashorn + + [15,18) + + + + org.openjdk.nashorn + nashorn-core + 15.3 + test + + + + + org.clojure @@ -81,7 +81,7 @@ - 1.8.0 + 1.8.0 @@ -96,7 +96,7 @@ scm:git:git@github.com:clojure/data.xml.git scm:git:git@github.com:clojure/data.xml.git git@github.com:clojure/data.xml.git - HEAD + v0.2.0-alpha7 From 377630e864de88aa9ecbf5ca302c681309e71ace Mon Sep 17 00:00:00 2001 From: Clojure Build Date: Fri, 5 Aug 2022 12:12:58 -0500 Subject: [PATCH 207/237] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index de07836..e1593d6 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ 4.0.0 data.xml - 0.2.0-alpha7 + 0.2.0-SNAPSHOT data.xml jar Functions to parse XML into lazy sequences and lazy trees and emit these as text @@ -96,7 +96,7 @@ scm:git:git@github.com:clojure/data.xml.git scm:git:git@github.com:clojure/data.xml.git git@github.com:clojure/data.xml.git - v0.2.0-alpha7 + HEAD From b6f00151aeff0fc5ae1cf995ba52ce9677c65288 Mon Sep 17 00:00:00 2001 From: Fogus Date: Fri, 5 Aug 2022 13:53:51 -0400 Subject: [PATCH 208/237] updating changes and readme --- CHANGES.md | 6 ++++-- README.md | 8 ++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 02d268c..f3c0315 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,8 @@ -From 0.2.0-alpha6 to ??? +From 0.2.0-alpha7 to ??? +- +From 0.2.0-alpha6 to 0.2.0-alpha7 - Replace data.codec with using Base64, now in the JDK - +- Opt out of namespace awareness in the event-seq function by passing :namespace-aware mapped to a truthy value in the opts map From 0.2.0-alpha5 to 0.2.0-alpha6 - ClojureScript implementation fixes and tests diff --git a/README.md b/README.md index 9414581..f91c7ea 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Please report bugs using JIRA [here](https://clojure.atlassian.net/browse/DXML). Latest stable release: `0.0.8` -Latest preview release: `0.2.0-alpha6` +Latest preview release: `0.2.0-alpha7` (The main features of the `0.2.0` series are XML Namespace support and Clojurescript support) @@ -49,7 +49,7 @@ For Maven projects, add the following XML in your `pom.xml`'s `` s org.clojure data.xml - 0.2.0-alpha6 + 0.2.0-alpha7 ### Leiningen @@ -63,7 +63,7 @@ Add the following to the `project.clj` dependencies: For preview: - [org.clojure/data.xml "0.2.0-alpha6"] + [org.clojure/data.xml "0.2.0-alpha7"] ### [CLI/`deps.edn`](https://clojure.org/reference/deps_and_cli) @@ -73,7 +73,7 @@ Add the following to the `deps.edn` dependencies: org.clojure/data.xml {:mvn/version "0.0.8"} ;; for preview version: -org.clojure/data.xml {:mvn/version "0.2.0-alpha6"} +org.clojure/data.xml {:mvn/version "0.2.0-alpha7"} ``` ## Examples From d54b0a27c68abbdc3064e55ebd10942e6970c75e Mon Sep 17 00:00:00 2001 From: David Chelimsky Date: Wed, 20 Jul 2022 13:45:38 -0500 Subject: [PATCH 209/237] only add namespace to tags when namespace-aware is true --- src/main/clojure/clojure/data/xml.clj | 3 ++- src/main/clojure/clojure/data/xml/jvm/parse.clj | 2 +- src/main/clojure/clojure/data/xml/name.cljc | 2 +- src/test/clojure/clojure/data/xml/test_names.clj | 15 ++++++++++----- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/main/clojure/clojure/data/xml.clj b/src/main/clojure/clojure/data/xml.clj index 39279c4..b981cc9 100644 --- a/src/main/clojure/clojure/data/xml.clj +++ b/src/main/clojure/clojure/data/xml.clj @@ -76,7 +76,8 @@ for documentation on xml options. These are the defaults: (let [props* (merge {:include-node? #{:element :characters} :coalescing true :supporting-external-entities false - :location-info true} + :location-info true + :namespace-aware true} opts)] (pull-seq (make-stream-reader props* source) props* diff --git a/src/main/clojure/clojure/data/xml/jvm/parse.clj b/src/main/clojure/clojure/data/xml/jvm/parse.clj index 1b88ffd..bb32c46 100644 --- a/src/main/clojure/clojure/data/xml/jvm/parse.clj +++ b/src/main/clojure/clojure/data/xml/jvm/parse.clj @@ -79,7 +79,7 @@ XMLStreamConstants/START_ELEMENT (if (include-node? :element) (let [ns-env (nss-hash sreader (or (first ns-envs) pu/EMPTY)) - tag (qname (when-not namespace-aware (.getNamespaceURI sreader)) + tag (qname (when namespace-aware (.getNamespaceURI sreader)) (.getLocalName sreader) (.getPrefix sreader)) attrs (attr-hash sreader) diff --git a/src/main/clojure/clojure/data/xml/name.cljc b/src/main/clojure/clojure/data/xml/name.cljc index 834c9fd..97d1c62 100644 --- a/src/main/clojure/clojure/data/xml/name.cljc +++ b/src/main/clojure/clojure/data/xml/name.cljc @@ -51,7 +51,7 @@ ([uri local] (keyword (when-not (str/blank? uri) (encode-uri (str "xmlns." uri))) local)) - ([uri local prefix] (qname uri local))) + ([uri local _prefix] (qname uri local))) ;; The empty string shall be equal to nil for xml names (defn namespaced? [qn] diff --git a/src/test/clojure/clojure/data/xml/test_names.clj b/src/test/clojure/clojure/data/xml/test_names.clj index 1cca97e..c3bc9d0 100644 --- a/src/test/clojure/clojure/data/xml/test_names.clj +++ b/src/test/clojure/clojure/data/xml/test_names.clj @@ -16,7 +16,7 @@ ["uri-u:" "name"] [::U/name "{uri-u:}name" (parse-qname "{uri-u:}name") (as-qname "{uri-u:}name")] ["uri-v:" "vname"] [::V/vname "{uri-v:}vname" (parse-qname "{uri-v:}vname")] ["uri-w:" "wname"] [::W/wname "{uri-w:}wname" (parse-qname "{uri-w:}wname")] - ;; ["http://www.w3.org/XML/1998/namespace" "name"] [:xml/name] + ["http://www.w3.org/XML/1998/namespace" "name"] [:xml/name] ["http://www.w3.org/2000/xmlns/" "name"] [:xmlns/name])) @@ -27,10 +27,15 @@ "100")) (deftest test-parse-raw - (are [xml result] (= (parse-str xml) result) - "100" - (element ::D/limit {} - (element ::D/nresults nil "100")))) + (testing "includes namespace in tags when namespace-aware is true" + (is (= (element ::D/limit {} + (element ::D/nresults nil "100")) + (parse-str "100")))) + (testing "leaves namespace off tags when namespace-aware is false" + (is (= (element :limit {:xmlns.http%3A%2F%2Fwww.w3.org%2F2000%2Fxmlns%2F/D "DAV:"} + (element :nresults nil "100")) + (parse-str "100" + :namespace-aware false))))) (deftest qnames (is (= (qname "foo") (as-qname :foo)))) From 4fbff240e0e4d57537b616fc4c2b7f28f6555e20 Mon Sep 17 00:00:00 2001 From: Clojure Build Date: Fri, 16 Sep 2022 11:52:51 -0500 Subject: [PATCH 210/237] [maven-release-plugin] prepare release v0.2.0-alpha8 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e1593d6..b4156f9 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ 4.0.0 data.xml - 0.2.0-SNAPSHOT + 0.2.0-alpha8 data.xml jar Functions to parse XML into lazy sequences and lazy trees and emit these as text @@ -96,7 +96,7 @@ scm:git:git@github.com:clojure/data.xml.git scm:git:git@github.com:clojure/data.xml.git git@github.com:clojure/data.xml.git - HEAD + v0.2.0-alpha8 From ff85b409f975e43a686414e799f2aa1e494c92d7 Mon Sep 17 00:00:00 2001 From: Clojure Build Date: Fri, 16 Sep 2022 11:52:51 -0500 Subject: [PATCH 211/237] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index b4156f9..e703940 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ 4.0.0 data.xml - 0.2.0-alpha8 + 0.2.1-SNAPSHOT data.xml jar Functions to parse XML into lazy sequences and lazy trees and emit these as text @@ -96,7 +96,7 @@ scm:git:git@github.com:clojure/data.xml.git scm:git:git@github.com:clojure/data.xml.git git@github.com:clojure/data.xml.git - v0.2.0-alpha8 + HEAD From 35c0758a617e742d062e3eee9242c74971d0d0db Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Wed, 23 Nov 2022 09:52:28 -0600 Subject: [PATCH 212/237] changelog format update --- CHANGES.md | 40 +++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index f3c0315..d31f421 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,16 +1,23 @@ -From 0.2.0-alpha7 to ??? -- -From 0.2.0-alpha6 to 0.2.0-alpha7 +## 0.2.0-alpha8 + +- When parsing, only include namespace in tags when :namespace-aware is true + +## 0.2.0-alpha7 + - Replace data.codec with using Base64, now in the JDK - Opt out of namespace awareness in the event-seq function by passing :namespace-aware mapped to a truthy value in the opts map -From 0.2.0-alpha5 to 0.2.0-alpha6 + +## 0.2.0-alpha6 + - ClojureScript implementation fixes and tests -From 0.2.0-alpha3 to 0.2.0-alpha5 +## 0.2.0-alpha5 + - Fix error check for builtin prefixes DXML-49 - Remove reflection cases DXML-46 -From 0.2.0-alpha2 to 0.2.0-alpha3 +## 0.2.0-alpha3 + - Minimum requirement is now clojure 1.7.0 - Print newline after preamble when pretty-printing (DXML-35) - Serialize built-in data types in XML Schema (DXML-27) @@ -21,14 +28,16 @@ From 0.2.0-alpha2 to 0.2.0-alpha3 - Support empty protocol function on Element deftypes (DXML-44) - Reflection cleanup (DXML-42) -From 0.2.0-alpha1 to 0.2.0-alpha2 +## 0.2.0-alpha2 + - qname function now returns canonical (keyword) names - Remove QName defrecord from Clojurescript - Rename canonical-name to as-qname - Remove to-qname - xml nodes now implement map equality -From 0.1.0-beta3 to 0.2.0-alpha1 +## 0.2.0-alpha1 + - Define uniform mapping of xml namespaces to clojure namespaces via percent-encoding - Remove declare-ns and alias-ns - Introduce alias-uri @@ -36,28 +45,33 @@ From 0.1.0-beta3 to 0.2.0-alpha1 - data.xml now requires Clojure 1.5.0+ (due to percent-sign in keywords) - Preserve whitespace by default -From 0.1.0-beta2 to 0.1.0-beta3 +## 0.1.0-beta3 + - Fix emitter to keep non-namespaced xml names out of any set default namespace - Add support for location info in parser -From 0.1.0-beta1 to 0.1.0-beta2 +## 0.1.0-beta2 + - Add support for emitting DOCTYPEs (DXML-10) - Fix issue emitting sibling namespaces (DXML-33) - Fix issue printing defaulted namespaces (DXML-30) -From 0.0.8 to 0.1.0-beta1 +## 0.1.0-beta1 + - Add support for XML namespaces (DXML-4) - Fix pull-seq so it produces character events that work with emit-events (DXML-28) - Removed docs and references to JDK 1.5, data.xml now requires 1.6+ - data.xml now requires Clojure 1.4.0+ -From 0.0.7 to 0.0.8 +## 0.0.8 + - Remove relection warnings in emit-cdata (DXML-16) - Added an EPL license file (DXML-19) - Fixed bug in the handling of CData end tags (DXML-17) - Added support for emitting booleans and numbers (DXML-14) -From 0.0.6 to 0.0.7 +## 0.0.7 + - Fixed bug with args to the indentation function (DXML-7) - Strings now supported as tag names, previously was only kewords (DXML-8) - Add CDATA and comments support to sexp-as-element (DXML-11) From 46c042acd6e96ba68f0d9dc60f9537c27cbbbd70 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Wed, 23 Nov 2022 09:53:15 -0600 Subject: [PATCH 213/237] update README for last release --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f91c7ea..ebb993f 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Please report bugs using JIRA [here](https://clojure.atlassian.net/browse/DXML). Latest stable release: `0.0.8` -Latest preview release: `0.2.0-alpha7` +Latest preview release: `0.2.0-alpha8` (The main features of the `0.2.0` series are XML Namespace support and Clojurescript support) @@ -49,7 +49,7 @@ For Maven projects, add the following XML in your `pom.xml`'s `` s org.clojure data.xml - 0.2.0-alpha7 + 0.2.0-alpha8 ### Leiningen @@ -63,7 +63,7 @@ Add the following to the `project.clj` dependencies: For preview: - [org.clojure/data.xml "0.2.0-alpha7"] + [org.clojure/data.xml "0.2.0-alpha8"] ### [CLI/`deps.edn`](https://clojure.org/reference/deps_and_cli) @@ -73,7 +73,7 @@ Add the following to the `deps.edn` dependencies: org.clojure/data.xml {:mvn/version "0.0.8"} ;; for preview version: -org.clojure/data.xml {:mvn/version "0.2.0-alpha7"} +org.clojure/data.xml {:mvn/version "0.2.0-alpha8"} ``` ## Examples From 891855d48dc80cd2552b8a6d9b146ad37e911ea9 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Wed, 23 Nov 2022 10:08:00 -0600 Subject: [PATCH 214/237] change readme examples to not :refer :all --- README.md | 134 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 69 insertions(+), 65 deletions(-) diff --git a/README.md b/README.md index ebb993f..f40ad12 100644 --- a/README.md +++ b/README.md @@ -78,9 +78,9 @@ org.clojure/data.xml {:mvn/version "0.2.0-alpha8"} ## Examples -The examples below assume you have added a `:refer :all` for data.xml: +The examples below assume you have added a `:refer` for data.xml: - (require '[clojure.data.xml :refer :all]) + (require '[clojure.data.xml :as xml]) data.xml supports parsing and emitting XML. The parsing functions will read XML from a @@ -90,7 +90,7 @@ or (let [input-xml (java.io.StringReader. " The baz value")] - (parse input-xml)) + (xml/parse input-xml)) #clojure.data.xml.Element{:tag :foo, :attrs {}, @@ -104,27 +104,27 @@ The data is returned as defrecords and can be manipulated using the normal clojure data structure functions. Additional parsing options can be passed via key pairs: - (parse-str "" :coalescing false) + (xml/parse-str "" :coalescing false) #clojure.data.xml.Element{:tag :a, :attrs {}, :content ("\nfoo bar\n" "\nbaz\n")} XML elements can be created using the typical defrecord constructor functions or the element function used below or just a plain map with :tag :attrs :content keys, and written using a [java.io.Writer](https://docs.oracle.com/javase/8/docs/api/java/io/Writer.html).: - (let [tags (element :foo {:foo-attr "foo value"} - (element :bar {:bar-attr "bar value"} - (element :baz {} "The baz value")))] + (let [tags (xml/element :foo {:foo-attr "foo value"} + (xml/element :bar {:bar-attr "bar value"} + (xml/element :baz {} "The baz value")))] (with-open [out-file (java.io.FileWriter. "/tmp/foo.xml")] - (emit tags out-file))) + (xml/emit tags out-file))) ;;-> Writes XML to /tmp/foo.xml The same can also be expressed using a more Hiccup-like style of defining the elements using sexp-as-element: - (= (element :foo {:foo-attr "foo value"} - (element :bar {:bar-attr "bar value"} - (element :baz {} "The baz value"))) - (sexp-as-element + (= (xml/element :foo {:foo-attr "foo value"} + (xml/element :bar {:bar-attr "bar value"} + (xml/element :baz {} "The baz value"))) + (xml/sexp-as-element [:foo {:foo-attr "foo value"} [:bar {:bar-attr "bar value"} [:baz {} "The baz value"]]])) @@ -132,41 +132,41 @@ The same can also be expressed using a more Hiccup-like style of defining the el Comments and CDATA can also be emitted as an S-expression with the special tag names :-cdata and :-comment: - (= (element :tag {:attr "value"} - (element :body {} (cdata "not parsed true XML can be "round tripped" through the library: - (let [tags (element :foo {:foo-attr "foo value"} - (element :bar {:bar-attr "bar value"} - (element :baz {} "The baz value")))] + (let [tags (xml/element :foo {:foo-attr "foo value"} + (xml/element :bar {:bar-attr "bar value"} + (xml/element :baz {} "The baz value")))] (with-open [out-file (java.io.FileWriter. "/tmp/foo.xml")] - (emit tags out-file)) + (xml/emit tags out-file)) (with-open [input (java.io.FileInputStream. "/tmp/foo.xml")] - (parse input))) + (xml/parse input))) #clojure.data.xml.Element{:tag :foo, :attrs {:foo-attr "foo value"}...} There are also some string based functions that are useful for debugging. - (let [tags (element :foo {:foo-attr "foo value"} - (element :bar {:bar-attr "bar value"} - (element :baz {} "The baz value")))] - (= tags (parse-str (emit-str tags)))) + (let [tags (xml/element :foo {:foo-attr "foo value"} + (xml/element :bar {:bar-attr "bar value"} + (xml/element :baz {} "The baz value")))] + (= tags (xml/parse-str (xml/emit-str tags)))) true Indentation is supported, but should be treated as a debugging feature as it's likely to be pretty slow: - (print (indent-str (element :foo {:foo-attr "foo value"} - (element :bar {:bar-attr "bar value"} - (element :baz {} "The baz value1") - (element :baz {} "The baz value2") - (element :baz {} "The baz value3"))))) + (print (xml/indent-str (xml/element :foo {:foo-attr "foo value"} + (xml/element :bar {:bar-attr "bar value"} + (xml/element :baz {} "The baz value1") + (xml/element :baz {} "The baz value2") + (xml/element :baz {} "The baz value3"))))) @@ -179,33 +179,34 @@ as it's likely to be pretty slow: CDATA can be emitted: - (emit-str (element :foo {} - (cdata ""))) + (xml/emit-str (xml/element :foo {} + (xml/cdata ""))) "]]>" But will be read as regular character data: - (parse-str (emit-str (element :foo {} - (cdata "")))) + (xml/parse-str (xml/emit-str (element :foo {} + (xml/cdata "")))) #clojure.data.xml.Element{:tag :foo, :attrs {}, :content ("")} Comments can also be emitted: - (emit-str (element :foo {} - (xml-comment "Just a goes here") - (element :bar {} "and another element"))) + (xml/emit-str + (xml/element :foo {} + (xml/xml-comment "Just a goes here") + (xml/element :bar {} "and another element"))) "and another element" But are ignored when read: - (emit-str - (parse-str - (emit-str (element :foo {} - (xml-comment "Just a goes here") - (element :bar {} "and another element"))))) + (xml/emit-str + (xml/parse-str + (xml/emit-str (xml/element :foo {} + (xml/xml-comment "Just a goes here") + (xml/element :bar {} "and another element"))))) "and another element" @@ -215,8 +216,8 @@ XML Namespaced names (QNames) are encoded into clojure keywords, by percent-enco Below is an example of parsing an XHTML document: - (parse-str " - ") + (xml/parse-str " + ") #...Element{:tag :xmlns.http%3A%2F%2Fwww.w3.org%2F1999%2Fxhtml/html, :attrs {}, @@ -224,10 +225,10 @@ Below is an example of parsing an XHTML document: Emitting namespaced XML is usually done by using `alias-uri` in combination with clojure's built-in `::kw-ns/shorthands`: - (alias-uri 'xh "http://www.w3.org/1999/xhtml") + (xml/alias-uri 'xh "http://www.w3.org/1999/xhtml") - (emit-str {:tag ::xh/html - :content [{:tag ::xh/head} {:tag ::xh/body :content ["DOCUMENT"]}]}) + (xml/emit-str {:tag ::xh/html + :content [{:tag ::xh/head} {:tag ::xh/body :content ["DOCUMENT"]}]}) @@ -237,8 +238,8 @@ Emitting namespaced XML is usually done by using `alias-uri` in combination with It is also allowable to use `javax.xml.namespace.QName` instances, as well as strings with the informal `{ns}n` encoding. - (emit-str {:tag (qname "http://www.w3.org/1999/xhtml" "html")}) - (emit-str {:tag "{http://www.w3.org/1999/xhtml}html"}) + (xml/emit-str {:tag (qname "http://www.w3.org/1999/xhtml" "html")}) + (xml/emit-str {:tag "{http://www.w3.org/1999/xhtml}html"}) @@ -248,16 +249,18 @@ Prefixes are mostly an artifact of xml serialisation. They can be customized by explicitly declaring them as attributes in the `xmlns` kw-namespace: - (emit-str (element (qname "http://www.w3.org/1999/xhtml" "title") - {:xmlns/foo "http://www.w3.org/1999/xhtml"} - "Example title")) + (xml/emit-str + (xml/element (xml/qname "http://www.w3.org/1999/xhtml" "title") + {:xmlns/foo "http://www.w3.org/1999/xhtml"} + "Example title")) "Example title" Not specifying a namespace prefix will results in a prefix being generated: - (emit-str (element ::xh/title - {} - "Example title")) + (xml/emit-str + (xml/element ::xh/title + {} + "Example title")) "Example title" @@ -265,24 +268,25 @@ The above example auto assigns prefixes for the namespaces used. In this case it was named `a` by the emitter. Emitting several nested tags with the same namespace will use one prefix: - (emit-str (element ::xh/html - {} - (element ::xh/head + (xml/emit-str + (xml/element ::xh/html + {} + (xml/element ::xh/head {} - (element ::xh/title - {} - "Example title")))) + (xml/element ::xh/title + {} + "Example title")))) "Example title" Note that the jdk QName ignores namespace prefixes for equality, but allows to preserve them for emitting. - (= (parse-str "Example title") - (parse-str "Example title")) + (= (xml/parse-str "Example title") + (xml/parse-str "Example title")) In data.xml prefix mappings are (by default) retained in metadata on a tag record. If there is no metadata, new prefixes will be generated when emitting. - (emit-str (parse-str "")) + (xml/emit-str (xml/parse-str "")) ## Location information as meta @@ -293,11 +297,11 @@ By default the parser attaches location information as element meta, (deftest test-location-meta (let [input "\n" location-meta (comp :clojure.data.xml/location-info meta)] - (is (= 1 (-> input parse-str location-meta :line-number))) + (is (= 1 (-> input xml/parse-str location-meta :line-number))) To elide location information, pass `:location-info false` to the parser: - (parse-str your-input :location-info false) + (xml/parse-str your-input :location-info false) ## Clojurescript support From 6a66ee5213aba0b4a20e6e294426b60196449876 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Wed, 23 Nov 2022 10:21:52 -0600 Subject: [PATCH 215/237] check and refresh all examples --- README.md | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index f40ad12..5cf0e2f 100644 --- a/README.md +++ b/README.md @@ -92,20 +92,17 @@ or The baz value")] (xml/parse input-xml)) - #clojure.data.xml.Element{:tag :foo, - :attrs {}, - :content (#clojure.data.xml.Element{:tag :bar, - :attrs {}, - :content (#clojure.data.xml.Element{:tag :baz, - :attrs {}, - :content ("The baz value")})})} + #xml/element{:tag :foo, + :content [#xml/element{:tag :bar, + :content [#xml/element{:tag :baz, + :content ["The baz value"]}]}]} The data is returned as defrecords and can be manipulated using the normal clojure data structure functions. Additional parsing options can be passed via key pairs: (xml/parse-str "" :coalescing false) - #clojure.data.xml.Element{:tag :a, :attrs {}, :content ("\nfoo bar\n" "\nbaz\n")} + #xml/element{:tag :a, :content ["\nfoo bar\n" "\nbaz\n"]} XML elements can be created using the typical defrecord constructor functions or the element function used below or just a plain map with :tag :attrs :content keys, and written using a @@ -134,7 +131,7 @@ Comments and CDATA can also be emitted as an S-expression with the special tag n (= (xml/element :tag {:attr "value"} (xml/element :body {} (xml/cdata "not parsed true XML can be "round tripped" through the library: @@ -147,7 +144,7 @@ XML can be "round tripped" through the library: (with-open [input (java.io.FileInputStream. "/tmp/foo.xml")] (xml/parse input))) - #clojure.data.xml.Element{:tag :foo, :attrs {:foo-attr "foo value"}...} + #xml/element{:tag :foo, :attrs {:foo-attr "foo value"}...} There are also some string based functions that are useful for debugging. @@ -186,10 +183,10 @@ CDATA can be emitted: But will be read as regular character data: - (xml/parse-str (xml/emit-str (element :foo {} + (xml/parse-str (xml/emit-str (xml/element :foo {} (xml/cdata "")))) - #clojure.data.xml.Element{:tag :foo, :attrs {}, :content ("")} + #xml/element{:tag :foo, :content [""]} Comments can also be emitted: @@ -219,9 +216,7 @@ Below is an example of parsing an XHTML document: (xml/parse-str " ") - #...Element{:tag :xmlns.http%3A%2F%2Fwww.w3.org%2F1999%2Fxhtml/html, - :attrs {}, - :content ()} + #xml/element{:tag :xmlns.http%3A%2F%2Fwww.w3.org%2F1999%2Fxhtml/html} Emitting namespaced XML is usually done by using `alias-uri` in combination with clojure's built-in `::kw-ns/shorthands`: @@ -238,7 +233,7 @@ Emitting namespaced XML is usually done by using `alias-uri` in combination with It is also allowable to use `javax.xml.namespace.QName` instances, as well as strings with the informal `{ns}n` encoding. - (xml/emit-str {:tag (qname "http://www.w3.org/1999/xhtml" "html")}) + (xml/emit-str {:tag (xml/qname "http://www.w3.org/1999/xhtml" "html")}) (xml/emit-str {:tag "{http://www.w3.org/1999/xhtml}html"}) @@ -297,7 +292,7 @@ By default the parser attaches location information as element meta, (deftest test-location-meta (let [input "\n" location-meta (comp :clojure.data.xml/location-info meta)] - (is (= 1 (-> input xml/parse-str location-meta :line-number))) + (is (= 1 (-> input xml/parse-str location-meta :line-number))))) To elide location information, pass `:location-info false` to the parser: From 0c3a9e01cb9b0ce04c71644ed9acca92f00237c8 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Wed, 23 Nov 2022 10:40:15 -0600 Subject: [PATCH 216/237] make example results easier to read --- README.md | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 5cf0e2f..c220fb3 100644 --- a/README.md +++ b/README.md @@ -179,7 +179,9 @@ CDATA can be emitted: (xml/emit-str (xml/element :foo {} (xml/cdata ""))) - "]]>" + ;; newlines added for readability, not in actual output + " + ]]>" But will be read as regular character data: @@ -195,7 +197,9 @@ Comments can also be emitted: (xml/xml-comment "Just a goes here") (xml/element :bar {} "and another element"))) - "and another element" + ;; newlines added for readability, not in actual output + " + and another element" But are ignored when read: @@ -205,7 +209,9 @@ But are ignored when read: (xml/xml-comment "Just a goes here") (xml/element :bar {} "and another element"))))) - "and another element" + ;; newlines added for readability, not in actual output + " + and another element" ## Namespace Support @@ -236,7 +242,9 @@ It is also allowable to use `javax.xml.namespace.QName` instances, as well as st (xml/emit-str {:tag (xml/qname "http://www.w3.org/1999/xhtml" "html")}) (xml/emit-str {:tag "{http://www.w3.org/1999/xhtml}html"}) - + ;; newlines added for readability, not in actual output + + ### Namespace Prefixes @@ -248,7 +256,10 @@ kw-namespace: (xml/element (xml/qname "http://www.w3.org/1999/xhtml" "title") {:xmlns/foo "http://www.w3.org/1999/xhtml"} "Example title")) - "Example title" + + ;; newlines added for readability, not in actual output + " + Example title" Not specifying a namespace prefix will results in a prefix being generated: @@ -257,7 +268,9 @@ Not specifying a namespace prefix will results in a prefix being generated: {} "Example title")) - "Example title" + ;; newlines added for readability, not in actual output + " + Example title" The above example auto assigns prefixes for the namespaces used. In this case it was named `a` by the emitter. Emitting several nested @@ -272,7 +285,11 @@ tags with the same namespace will use one prefix: {} "Example title")))) - "Example title" + ;; newlines and indents added for readability, not in actual output + " + + + Example title" Note that the jdk QName ignores namespace prefixes for equality, but allows to preserve them for emitting. From 3a6e66ddf7ecc570836f7c461c84616ea8345f90 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Wed, 23 Nov 2022 11:09:46 -0600 Subject: [PATCH 217/237] add example using xmlns and emitted unqualified tags --- README.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/README.md b/README.md index c220fb3..833cf48 100644 --- a/README.md +++ b/README.md @@ -226,6 +226,8 @@ Below is an example of parsing an XHTML document: Emitting namespaced XML is usually done by using `alias-uri` in combination with clojure's built-in `::kw-ns/shorthands`: + ;; this needs to be at the top level of your code (parallel to defns) + ;; or subsequent ::xh/ ... will throw "Invalid token" (xml/alias-uri 'xh "http://www.w3.org/1999/xhtml") (xml/emit-str {:tag ::xh/html @@ -237,6 +239,35 @@ Emitting namespaced XML is usually done by using `alias-uri` in combination with DOCUMENT +To emit namespaced tags without prefixes, you can also set the default xmlns at the root (it's important that the uris match!!): + + ;; at top level + (xml/alias-uri 'xh "http://www.w3.org/1999/xhtml") + + ;; top-level element should set xmlns that matches + (xml/emit-str + (xml/element ::xh/html + {:xmlns "http://www.w3.org/1999/xhtml"} + (xml/element ::xh/head) + (xml/element ::xh/body {} "DOCUMENT"))) + + ;; newlines and indents added for readability, not in actual output + " + + DOCUMENT + " + +Same example, but using the more concise hiccup style (same output): + + ;; at top level + (xml/alias-uri 'xh "http://www.w3.org/1999/xhtml") + + (xml/emit-str + (xml/sexp-as-element + [::xh/html {:xmlns "http://www.w3.org/1999/xhtml"} + [::xh/head] + [::xh/body "DOCUMENT"]])) + It is also allowable to use `javax.xml.namespace.QName` instances, as well as strings with the informal `{ns}n` encoding. (xml/emit-str {:tag (xml/qname "http://www.w3.org/1999/xhtml" "html")}) From 514e7b527dad9231cf14845874166d943eb13d37 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Wed, 23 Nov 2022 11:11:43 -0600 Subject: [PATCH 218/237] fix indent in example --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 833cf48..7cf5f1c 100644 --- a/README.md +++ b/README.md @@ -253,7 +253,8 @@ To emit namespaced tags without prefixes, you can also set the default xmlns at ;; newlines and indents added for readability, not in actual output " - + + DOCUMENT " From 67894742ff164a8bed81be5643a1f88acaaf9408 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Wed, 23 Nov 2022 11:13:05 -0600 Subject: [PATCH 219/237] fix indent --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7cf5f1c..2ba9fe2 100644 --- a/README.md +++ b/README.md @@ -266,8 +266,8 @@ Same example, but using the more concise hiccup style (same output): (xml/emit-str (xml/sexp-as-element [::xh/html {:xmlns "http://www.w3.org/1999/xhtml"} - [::xh/head] - [::xh/body "DOCUMENT"]])) + [::xh/head] + [::xh/body "DOCUMENT"]])) It is also allowable to use `javax.xml.namespace.QName` instances, as well as strings with the informal `{ns}n` encoding. From e0c66458a20a77b921bfb4713297c74bbbb36d2b Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Fri, 26 May 2023 15:18:13 -0500 Subject: [PATCH 220/237] add actions --- .github/workflows/release.yml | 19 +++++++++++++++++++ .github/workflows/snapshot.yml | 8 ++++++++ .github/workflows/test.yml | 7 +++++++ README.md | 3 +-- 4 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/snapshot.yml create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..e2718bd --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,19 @@ +name: Release on demand + +on: + workflow_dispatch: + inputs: + releaseVersion: + description: "Version to release" + required: true + snapshotVersion: + description: "Snapshot version after release" + required: true + +jobs: + call-release: + uses: clojure/build.ci/.github/workflows/release.yml@master + with: + releaseVersion: ${{ github.event.inputs.releaseVersion }} + snapshotVersion: ${{ github.event.inputs.snapshotVersion }} + secrets: inherit \ No newline at end of file diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml new file mode 100644 index 0000000..2472957 --- /dev/null +++ b/.github/workflows/snapshot.yml @@ -0,0 +1,8 @@ +name: Snapshot on demand + +on: [workflow_dispatch] + +jobs: + call-snapshot: + uses: clojure/build.ci/.github/workflows/snapshot.yml@master + secrets: inherit diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..1fa127c --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,7 @@ +name: Test + +on: [push] + +jobs: + call-test: + uses: clojure/build.ci/.github/workflows/test.yml@master diff --git a/README.md b/README.md index 2ba9fe2..ac5711a 100644 --- a/README.md +++ b/README.md @@ -389,8 +389,7 @@ Licensed under the [Eclipse Public License](http://www.opensource.org/licenses/e * [GitHub project](https://github.com/clojure/data.xml) * [Bug Tracker](https://clojure.atlassian.net/browse/DXML) -* [Continuous Integration](http://build.clojure.org/job/data.xml/) -* [Compatibility Test Matrix](http://build.clojure.org/job/data.xml-test-matrix/) +* [Continuous Integration](https://github.com/clojure/data.xml/actions/workflows/test.yml) ## Contributing From 00b44161c17c5b3b0b0c3e6c146f598de706ab42 Mon Sep 17 00:00:00 2001 From: JarrodCTaylor Date: Wed, 9 Aug 2023 18:28:49 -0500 Subject: [PATCH 221/237] Added github action to build api docs --- .github/workflows/doc-build.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .github/workflows/doc-build.yml diff --git a/.github/workflows/doc-build.yml b/.github/workflows/doc-build.yml new file mode 100644 index 0000000..d740808 --- /dev/null +++ b/.github/workflows/doc-build.yml @@ -0,0 +1,11 @@ + +name: Build API Docs + +on: + workflow_dispatch: + +jobs: + call-doc-build-workflow: + uses: clojure/build.ci/.github/workflows/doc-build.yml@master + with: + project: clojure/data.xml From 16693c89210b6e2ac82cc7bdf94ef583f64c028e Mon Sep 17 00:00:00 2001 From: JarrodCTaylor Date: Wed, 30 Aug 2023 22:21:16 -0500 Subject: [PATCH 222/237] Fix EPL license link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ac5711a..c9ed3b3 100644 --- a/README.md +++ b/README.md @@ -383,7 +383,7 @@ Make `extend-dom-as-data!` also support assoc, ... on dom nodes. ## License -Licensed under the [Eclipse Public License](http://www.opensource.org/licenses/eclipse-1.0.php). +Licensed under the [Eclipse Public License](https://opensource.org/license/epl-1-0/). ## Developer Information From 11d6a9fd37b4ee322b21a9a39e7319b81e08e0fe Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Mon, 19 Feb 2024 14:08:36 -0600 Subject: [PATCH 223/237] update parent pom --- pom.xml | 8 ++++---- project.clj | 15 +++++++++------ 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/pom.xml b/pom.xml index e703940..d2f76b3 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ Chouser chouser@n01se.net - http://chouser.n01se.net + https://chouser.n01se.net -5 @@ -32,7 +32,7 @@ org.clojure pom.contrib - 1.1.0 + 1.2.0 @@ -79,9 +79,9 @@ - - 1.8.0 + 1.9.0 diff --git a/project.clj b/project.clj index 517544b..920eadf 100644 --- a/project.clj +++ b/project.clj @@ -2,12 +2,15 @@ :source-paths ["src/main/clojure" "src/main/clojurescript"] :test-paths ["src/test/clojure" "src/test/clojurescript"] :resource-paths ["src/main/resources" "src/test/resources" "target/gen-resources"] - :dependencies [[org.clojure/clojure "1.10.0-beta8"] + :dependencies [[org.clojure/clojure "1.10.3"] [org.clojure/clojurescript "1.10.439"] - [cider/piggieback "0.5.3"] + [cider/piggieback "0.5.3"] [org.clojure/tools.nrepl "0.2.13"] - [org.clojure/test.check "0.9.0"] + [org.clojure/test.check "1.1.1"] [figwheel-sidecar "0.5.17"] - [binaryage/devtools "0.9.10"] - [org.openjdk.nashorn/nashorn-core "15.3"]] - :repl-options {:nrepl-middleware [cider.piggieback/wrap-cljs-repl]}) + [binaryage/devtools "0.9.10"] + [org.openjdk.nashorn/nashorn-core "15.3"]] + :repl-options {:nrepl-middleware [cider.piggieback/wrap-cljs-repl]} + :profiles {:dev {:dependencies [[com.github.nubank/morse "v2023.10.06.02"]] + :repositories [["jitpack" "https://jitpack.io"]]}} + ) From f5f552c38e76d9d34c6bc2e5b733fec61ad2ac7d Mon Sep 17 00:00:00 2001 From: clojure-build Date: Mon, 19 Feb 2024 20:10:50 +0000 Subject: [PATCH 224/237] [maven-release-plugin] prepare release v0.2.0-alpha9 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index d2f76b3..1d9cc09 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ 4.0.0 data.xml - 0.2.1-SNAPSHOT + 0.2.0-alpha9 data.xml jar Functions to parse XML into lazy sequences and lazy trees and emit these as text @@ -96,7 +96,7 @@ scm:git:git@github.com:clojure/data.xml.git scm:git:git@github.com:clojure/data.xml.git git@github.com:clojure/data.xml.git - HEAD + v0.2.0-alpha9 From 8d4a864c196467d72df31f1339454c9fd5716b19 Mon Sep 17 00:00:00 2001 From: clojure-build Date: Mon, 19 Feb 2024 20:10:50 +0000 Subject: [PATCH 225/237] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 1d9cc09..d2f76b3 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ 4.0.0 data.xml - 0.2.0-alpha9 + 0.2.1-SNAPSHOT data.xml jar Functions to parse XML into lazy sequences and lazy trees and emit these as text @@ -96,7 +96,7 @@ scm:git:git@github.com:clojure/data.xml.git scm:git:git@github.com:clojure/data.xml.git git@github.com:clojure/data.xml.git - v0.2.0-alpha9 + HEAD From 5c1f7941d2bd57680def2080da4ee8c4d9a3e1aa Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Mon, 19 Feb 2024 14:14:58 -0600 Subject: [PATCH 226/237] update for release --- CHANGES.md | 4 ++++ README.md | 10 +++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index d31f421..3afc68b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,7 @@ +## 0.2.0-alpha9 + +- Update parent pom, depend on Clojure 1.9.0 + ## 0.2.0-alpha8 - When parsing, only include namespace in tags when :namespace-aware is true diff --git a/README.md b/README.md index c9ed3b3..ba9fc16 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Please report bugs using JIRA [here](https://clojure.atlassian.net/browse/DXML). Latest stable release: `0.0.8` -Latest preview release: `0.2.0-alpha8` +Latest preview release: `0.2.0-alpha9` (The main features of the `0.2.0` series are XML Namespace support and Clojurescript support) @@ -49,7 +49,7 @@ For Maven projects, add the following XML in your `pom.xml`'s `` s org.clojure data.xml - 0.2.0-alpha8 + 0.2.0-alpha9 ### Leiningen @@ -63,7 +63,7 @@ Add the following to the `project.clj` dependencies: For preview: - [org.clojure/data.xml "0.2.0-alpha8"] + [org.clojure/data.xml "0.2.0-alpha9"] ### [CLI/`deps.edn`](https://clojure.org/reference/deps_and_cli) @@ -73,7 +73,7 @@ Add the following to the `deps.edn` dependencies: org.clojure/data.xml {:mvn/version "0.0.8"} ;; for preview version: -org.clojure/data.xml {:mvn/version "0.2.0-alpha8"} +org.clojure/data.xml {:mvn/version "0.2.0-alpha9"} ``` ## Examples @@ -394,6 +394,6 @@ Licensed under the [Eclipse Public License](https://opensource.org/license/epl-1 ## Contributing All contributions need to be made via patches attached to tickets in -[JIRA](http://clojure.atlassian.net/browse/DXML). Check the +[JIRA](https://clojure.atlassian.net/browse/DXML). Check the [Contributing to Clojure](https://clojure.org/community/contributing) page for more information. From 56d87f41b69ec85978d02c99c126eb6d0bddab2a Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Mon, 19 Feb 2024 14:24:32 -0600 Subject: [PATCH 227/237] remove dev profiles --- project.clj | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/project.clj b/project.clj index 920eadf..ff8e991 100644 --- a/project.clj +++ b/project.clj @@ -10,7 +10,4 @@ [figwheel-sidecar "0.5.17"] [binaryage/devtools "0.9.10"] [org.openjdk.nashorn/nashorn-core "15.3"]] - :repl-options {:nrepl-middleware [cider.piggieback/wrap-cljs-repl]} - :profiles {:dev {:dependencies [[com.github.nubank/morse "v2023.10.06.02"]] - :repositories [["jitpack" "https://jitpack.io"]]}} - ) + :repl-options {:nrepl-middleware [cider.piggieback/wrap-cljs-repl]}) From e6522752bd9d66b0d6c1255f1a706433975a92cc Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Mon, 19 Feb 2024 14:31:23 -0600 Subject: [PATCH 228/237] custom tests due to Nashorn --- .github/workflows/test.yml | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1fa127c..ee23055 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -3,5 +3,20 @@ name: Test on: [push] jobs: - call-test: - uses: clojure/build.ci/.github/workflows/test.yml@master + test: + strategy: + matrix: + os: [ubuntu-latest] # macOS-latest, windows-latest] + java-version: ["8"] + clojure-version: ["1.9.0", "1.10.3", "1.11.1"] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v3 + - name: Set up Java + uses: actions/setup-java@v3 + with: + java-version: ${{ matrix.java-version }} + distribution: 'temurin' + cache: 'maven' + - name: Build with Maven + run: mvn -ntp -B -Dclojure.version=${{ matrix.clojure-version }} clean test From 7cccbbbf913f55eb954f461cd2f222a231ebeee0 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Fri, 30 May 2025 15:42:34 -0500 Subject: [PATCH 229/237] update to new parent pom --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d2f76b3..e491cf9 100644 --- a/pom.xml +++ b/pom.xml @@ -32,7 +32,7 @@ org.clojure pom.contrib - 1.2.0 + 1.3.0 From 11da1b442209a4878c9b02aa8998f6a3389a36d9 Mon Sep 17 00:00:00 2001 From: Marco Biscaro Date: Mon, 8 Sep 2025 19:12:26 -0300 Subject: [PATCH 230/237] DXML-66 fix URLDecoder/URLEncoder reflective calls --- src/main/clojure/clojure/data/xml/jvm/name.clj | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/clojure/clojure/data/xml/jvm/name.clj b/src/main/clojure/clojure/data/xml/jvm/name.clj index 3f71c53..caab7c5 100644 --- a/src/main/clojure/clojure/data/xml/jvm/name.clj +++ b/src/main/clojure/clojure/data/xml/jvm/name.clj @@ -14,6 +14,8 @@ (javax.xml.namespace NamespaceContext QName) (java.net URLEncoder URLDecoder))) +(set! *warn-on-reflection* true) + (extend-protocol AsQName QName (qname-local [qname] (.getLocalPart qname)) @@ -32,8 +34,8 @@ (qname-uri [s] (.getNamespaceURI (parse-qname s)))) -(definline decode-uri [ns] +(definline decode-uri [^String ns] `(URLDecoder/decode ~ns "UTF-8")) -(definline encode-uri [uri] +(definline encode-uri [^String uri] `(URLEncoder/encode ~uri "UTF-8")) From 9037c8186a29f3415ae8a6730c0f8fbf5524135c Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Wed, 17 Sep 2025 16:44:12 -0500 Subject: [PATCH 231/237] update changelog --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 3afc68b..cf0accc 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,7 @@ +## 0.2.0-alpha10 + +- Fix reflection warnings in clojure.data.xml.name (DXML-66) + ## 0.2.0-alpha9 - Update parent pom, depend on Clojure 1.9.0 From 016d855515050c1e0e1bb1727fbfc45192d504b4 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Wed, 17 Sep 2025 16:56:30 -0500 Subject: [PATCH 232/237] update testing matrix --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ee23055..ed490a4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,7 +8,7 @@ jobs: matrix: os: [ubuntu-latest] # macOS-latest, windows-latest] java-version: ["8"] - clojure-version: ["1.9.0", "1.10.3", "1.11.1"] + clojure-version: ["1.9.0", "1.10.3", "1.11.4", "1.12.2"] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v3 From 96c45d8d3a64477d577ea2450fc006eef88befad Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Fri, 10 Oct 2025 13:41:15 -0500 Subject: [PATCH 233/237] Update test matrix --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ed490a4..ba208e6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,7 +8,7 @@ jobs: matrix: os: [ubuntu-latest] # macOS-latest, windows-latest] java-version: ["8"] - clojure-version: ["1.9.0", "1.10.3", "1.11.4", "1.12.2"] + clojure-version: ["1.9.0", "1.10.3", "1.11.4", "1.12.3"] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v3 From 7d59ec379e32a093d91207d0930fdd9eea9ecac2 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Tue, 30 Dec 2025 17:00:16 -0600 Subject: [PATCH 234/237] update permissions in workflows --- .github/workflows/doc-build.yml | 3 +++ .github/workflows/release.yml | 3 +++ .github/workflows/snapshot.yml | 3 +++ .github/workflows/test.yml | 3 +++ 4 files changed, 12 insertions(+) diff --git a/.github/workflows/doc-build.yml b/.github/workflows/doc-build.yml index d740808..9843948 100644 --- a/.github/workflows/doc-build.yml +++ b/.github/workflows/doc-build.yml @@ -1,6 +1,9 @@ name: Build API Docs +permissions: + contents: write + on: workflow_dispatch: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e2718bd..286cf95 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,5 +1,8 @@ name: Release on demand +permissions: + contents: write + on: workflow_dispatch: inputs: diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml index 2472957..9fdad8c 100644 --- a/.github/workflows/snapshot.yml +++ b/.github/workflows/snapshot.yml @@ -1,5 +1,8 @@ name: Snapshot on demand +permissions: + contents: read + on: [workflow_dispatch] jobs: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ba208e6..610647e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,5 +1,8 @@ name: Test +permissions: + contents: read + on: [push] jobs: From ed83a94e351cfa3200990c35336560e414d5471b Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Tue, 30 Dec 2025 17:03:29 -0600 Subject: [PATCH 235/237] update to latest parent pom --- CHANGES.md | 1 + README.md | 8 ++++---- pom.xml | 6 +++--- project.clj | 4 ++-- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index cf0accc..384af40 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,7 @@ ## 0.2.0-alpha10 - Fix reflection warnings in clojure.data.xml.name (DXML-66) +- Update parent pom, depend on Clojure 1.11.4 ## 0.2.0-alpha9 diff --git a/README.md b/README.md index ba9fc16..a00bc7a 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Please report bugs using JIRA [here](https://clojure.atlassian.net/browse/DXML). Latest stable release: `0.0.8` -Latest preview release: `0.2.0-alpha9` +Latest preview release: `0.2.0-alpha10` (The main features of the `0.2.0` series are XML Namespace support and Clojurescript support) @@ -49,7 +49,7 @@ For Maven projects, add the following XML in your `pom.xml`'s `` s org.clojure data.xml - 0.2.0-alpha9 + 0.2.0-alpha10 ### Leiningen @@ -63,7 +63,7 @@ Add the following to the `project.clj` dependencies: For preview: - [org.clojure/data.xml "0.2.0-alpha9"] + [org.clojure/data.xml "0.2.0-alpha10"] ### [CLI/`deps.edn`](https://clojure.org/reference/deps_and_cli) @@ -73,7 +73,7 @@ Add the following to the `deps.edn` dependencies: org.clojure/data.xml {:mvn/version "0.0.8"} ;; for preview version: -org.clojure/data.xml {:mvn/version "0.2.0-alpha9"} +org.clojure/data.xml {:mvn/version "0.2.0-alpha10"} ``` ## Examples diff --git a/pom.xml b/pom.xml index e491cf9..f8c0ba0 100644 --- a/pom.xml +++ b/pom.xml @@ -32,7 +32,7 @@ org.clojure pom.contrib - 1.3.0 + 1.4.0 @@ -56,7 +56,7 @@ org.clojure test.check - 1.1.1 + 1.1.3 test @@ -81,7 +81,7 @@ - 1.9.0 + 1.11.4 diff --git a/project.clj b/project.clj index ff8e991..794751c 100644 --- a/project.clj +++ b/project.clj @@ -2,11 +2,11 @@ :source-paths ["src/main/clojure" "src/main/clojurescript"] :test-paths ["src/test/clojure" "src/test/clojurescript"] :resource-paths ["src/main/resources" "src/test/resources" "target/gen-resources"] - :dependencies [[org.clojure/clojure "1.10.3"] + :dependencies [[org.clojure/clojure "1.11.4"] [org.clojure/clojurescript "1.10.439"] [cider/piggieback "0.5.3"] [org.clojure/tools.nrepl "0.2.13"] - [org.clojure/test.check "1.1.1"] + [org.clojure/test.check "1.1.3"] [figwheel-sidecar "0.5.17"] [binaryage/devtools "0.9.10"] [org.openjdk.nashorn/nashorn-core "15.3"]] From a80a32eb0d9d61f37e19735b1b13f59e12d77043 Mon Sep 17 00:00:00 2001 From: clojure-build Date: Tue, 30 Dec 2025 23:06:23 +0000 Subject: [PATCH 236/237] [maven-release-plugin] prepare release v0.2.0-alpha10 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index f8c0ba0..79daffd 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ 4.0.0 data.xml - 0.2.1-SNAPSHOT + 0.2.0-alpha10 data.xml jar Functions to parse XML into lazy sequences and lazy trees and emit these as text @@ -96,7 +96,7 @@ scm:git:git@github.com:clojure/data.xml.git scm:git:git@github.com:clojure/data.xml.git git@github.com:clojure/data.xml.git - HEAD + v0.2.0-alpha10 From 24a875b0e1899fb1945ac54174f59a8d3e05f06f Mon Sep 17 00:00:00 2001 From: clojure-build Date: Tue, 30 Dec 2025 23:06:23 +0000 Subject: [PATCH 237/237] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 79daffd..0943340 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ 4.0.0 data.xml - 0.2.0-alpha10 + 0.2.0-alpha11-SNAPSHOT data.xml jar Functions to parse XML into lazy sequences and lazy trees and emit these as text @@ -96,7 +96,7 @@ scm:git:git@github.com:clojure/data.xml.git scm:git:git@github.com:clojure/data.xml.git git@github.com:clojure/data.xml.git - v0.2.0-alpha10 + HEAD