diff options
author | Stuart Sierra <mail@stuartsierra.com> | 2010-02-01 10:49:45 -0500 |
---|---|---|
committer | Stuart Sierra <mail@stuartsierra.com> | 2010-02-01 12:42:47 -0500 |
commit | 6b68e4872211fe22752d35502b6111f693f812d7 (patch) | |
tree | 1c4125c9dce9e01246992dcc2bc4cee6e2dbb434 /src/main | |
parent | 4e7a55d4e7e899d29f725222495d752730584bdd (diff) |
Delete obsolete versions of popular libs.
Diffstat (limited to 'src/main')
-rw-r--r-- | src/main/clojure/clojure/contrib/json/read.clj | 343 | ||||
-rw-r--r-- | src/main/clojure/clojure/contrib/json/write.clj | 193 | ||||
-rw-r--r-- | src/main/clojure/clojure/contrib/str_utils.clj | 100 | ||||
-rw-r--r-- | src/main/clojure/clojure/contrib/str_utils2.clj | 373 |
4 files changed, 0 insertions, 1009 deletions
diff --git a/src/main/clojure/clojure/contrib/json/read.clj b/src/main/clojure/clojure/contrib/json/read.clj deleted file mode 100644 index 85a3b457..00000000 --- a/src/main/clojure/clojure/contrib/json/read.clj +++ /dev/null @@ -1,343 +0,0 @@ -;;; json/read.clj: JavaScript Object Notation (JSON) parser - -;; by Stuart Sierra, http://stuartsierra.com/ -;; February 13, 2009 - -;; Copyright (c) Stuart Sierra, 2009. 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. - - -;; Change Log -;; -;; February 13, 2009: added custom handler for quoted strings, to -;; allow escaped forward backslash characters ("\/") in strings. -;; -;; January 26, 2009: initial version - - -;; For more information on JSON, see http://www.json.org/ -;; -;; This library parses data in JSON format. This is a fairly strict -;; implementation of JSON as described at json.org, not a full-fledged -;; JavaScript parser. JavaScript functions and object constructors -;; are not supported. Object field names must be quoted strings; they -;; may not be bare symbols. - - - -(ns - #^{:author "Stuart Sierra", - :doc "JavaScript Object Notation (JSON) parser - - For more information on JSON, see http://www.json.org/ - - This library parses data in JSON format. This is a fairly strict - implementation of JSON as described at json.org, not a full-fledged - JavaScript parser. JavaScript functions and object constructors - are not supported. Object field names must be quoted strings; they - may not be bare symbols. - - If you want to convert map keys from strings to keywords, use - clojure.contrib.walk/keywordize-keys -", - :see-also [["http://www.json.org", "JSON Home Page"]]} - clojure.contrib.json.read - (:import (java.io PushbackReader StringReader Reader EOFException)) - (:use [clojure.test :only (deftest- is)])) - -(declare read-json) - -(def #^{:doc "If true, JSON object keys will be converted to keywords - instead of strings. Defaults to false. There are no checks that - the strings form valid keywords."} *json-keyword-keys* false) - -(defn- read-json-array [#^PushbackReader stream] - ;; Expects to be called with the head of the stream AFTER the - ;; opening bracket. - (loop [i (.read stream), result (transient [])] - (let [c (char i)] - (cond - (= i -1) (throw (EOFException. "JSON error (end-of-file inside array)")) - (Character/isWhitespace c) (recur (.read stream) result) - (= c \,) (recur (.read stream) result) - (= c \]) (persistent! result) - :else (do (.unread stream (int c)) - (let [element (read-json stream)] - (recur (.read stream) (conj! result element)))))))) - -(defn- read-json-object [#^PushbackReader stream] - ;; Expects to be called with the head of the stream AFTER the - ;; opening bracket. - (loop [i (.read stream), key nil, result (transient {})] - (let [c (char i)] - (cond - (= i -1) (throw (EOFException. "JSON error (end-of-file inside object)")) - - (Character/isWhitespace c) (recur (.read stream) key result) - - (= c \,) (recur (.read stream) nil result) - - (= c \:) (recur (.read stream) key result) - - (= c \}) (if (nil? key) - (persistent! result) - (throw (Exception. "JSON error (key missing value in object)"))) - - :else (do (.unread stream i) - (let [element (read-json stream)] - (if (nil? key) - (if (string? element) - (recur (.read stream) element result) - (throw (Exception. "JSON error (non-string key in object)"))) - (recur (.read stream) nil - (assoc! result (if *json-keyword-keys* (keyword key) key) - element))))))))) - -(defn- read-json-hex-character [#^PushbackReader stream] - ;; Expects to be called with the head of the stream AFTER the - ;; initial "\u". Reads the next four characters from the stream. - (let [digits [(.read stream) - (.read stream) - (.read stream) - (.read stream)]] - (when (some neg? digits) - (throw (EOFException. "JSON error (end-of-file inside Unicode character escape)"))) - (let [chars (map char digits)] - (when-not (every? #{\0 \1 \2 \3 \4 \5 \6 \7 \8 \9 \a \b \c \d \e \f \A \B \C \D \E \F} - chars) - (throw (Exception. "JSON error (invalid hex character in Unicode character escape)"))) - (char (Integer/parseInt (apply str chars) 16))))) - -(defn- read-json-escaped-character [#^PushbackReader stream] - ;; Expects to be called with the head of the stream AFTER the - ;; initial backslash. - (let [c (char (.read stream))] - (cond - (#{\" \\ \/} c) c - (= c \b) \backspace - (= c \f) \formfeed - (= c \n) \newline - (= c \r) \return - (= c \t) \tab - (= c \u) (read-json-hex-character stream)))) - -(defn- read-json-quoted-string [#^PushbackReader stream] - ;; Expects to be called with the head of the stream AFTER the - ;; opening quotation mark. - (let [buffer (StringBuilder.)] - (loop [i (.read stream)] - (let [c (char i)] - (cond - (= i -1) (throw (EOFException. "JSON error (end-of-file inside string)")) - (= c \") (str buffer) - (= c \\) (do (.append buffer (read-json-escaped-character stream)) - (recur (.read stream))) - :else (do (.append buffer c) - (recur (.read stream)))))))) - -(defn read-json - "Read one JSON record from s, which may be a String or a - java.io.PushbackReader." - ([] (read-json *in* true nil)) - ([s] (cond (string? s) - (read-json (PushbackReader. (StringReader. s)) true nil) - - (instance? PushbackReader s) - (read-json s true nil) - - (instance? Reader s) - (read-json (PushbackReader. s) true nil))) - ([#^PushbackReader stream eof-error? eof-value] - (loop [i (.read stream)] - (let [c (char i)] - (cond - ;; Handle end-of-stream - (= i -1) (if eof-error? - (throw (EOFException. "JSON error (end-of-file)")) - eof-value) - - ;; Ignore whitespace - (Character/isWhitespace c) (recur (.read stream)) - - ;; Read numbers, true, and false with Clojure reader - (#{\- \0 \1 \2 \3 \4 \5 \6 \7 \8 \9} c) - (do (.unread stream i) - (read stream true nil)) - - ;; Read strings - (= c \") (read-json-quoted-string stream) - - ;; Read null as nil - (= c \n) (let [ull [(char (.read stream)) - (char (.read stream)) - (char (.read stream))]] - (if (= ull [\u \l \l]) - nil - (throw (Exception. (str "JSON error (expected null): " c ull))))) - - ;; Read true - (= c \t) (let [rue [(char (.read stream)) - (char (.read stream)) - (char (.read stream))]] - (if (= rue [\r \u \e]) - true - (throw (Exception. (str "JSON error (expected true): " c rue))))) - - ;; Read false - (= c \f) (let [alse [(char (.read stream)) - (char (.read stream)) - (char (.read stream)) - (char (.read stream))]] - (if (= alse [\a \l \s \e]) - false - (throw (Exception. (str "JSON error (expected false): " c alse))))) - - - - ;; Read JSON objects - (= c \{) (read-json-object stream) - - ;; Read JSON arrays - (= c \[) (read-json-array stream) - - :else (throw (Exception. (str "JSON error (unexpected character): " c)))))))) - - -(defn read-json-string [string] - (read-json (PushbackReader. (StringReader. string)))) - - -;;; TESTS - -(deftest- can-read-numbers - (is (= 42 (read-json "42"))) - (is (= -3 (read-json "-3"))) - (is (= 3.14159 (read-json "3.14159"))) - (is (= 6.022e23 (read-json "6.022e23")))) - -(deftest- can-read-null - (is (= nil (read-json "null")))) - -(deftest- can-read-strings - (is (= "Hello, World!" (read-json "\"Hello, World!\"")))) - -(deftest- handles-escaped-slashes-in-strings - (is (= "/foo/bar" (read-json "\"\\/foo\\/bar\"")))) - -(deftest- handles-unicode-escapes - (is (= " \u0beb " (read-json "\" \\u0bEb \"")))) - -(deftest- handles-escaped-whitespace - (is (= "foo\nbar" (read-json "\"foo\\nbar\""))) - (is (= "foo\rbar" (read-json "\"foo\\rbar\""))) - (is (= "foo\tbar" (read-json "\"foo\\tbar\"")))) - -(deftest- can-read-booleans - (is (= true (read-json "true"))) - (is (= false (read-json "false")))) - -(deftest- can-ignore-whitespace - (is (= nil (read-json "\r\n null")))) - -(deftest- can-read-arrays - (is (= [1 2 3] (read-json "[1,2,3]"))) - (is (= ["Ole" "Lena"] (read-json "[\"Ole\", \r\n \"Lena\"]")))) - -(deftest- can-read-objects - (is (= {"a" 1, "b" 2} (read-json "{\"a\": 1, \"b\": 2}")))) - -(deftest- can-read-nested-structures - (is (= {"a" [1 2 {"b" [3 "four"]} 5.5]} - (read-json "{\"a\":[1,2,{\"b\":[3,\"four\"]},5.5]}")))) - -(deftest- disallows-non-string-keys - (is (thrown? Exception (read-json "{26:\"z\"")))) - -(deftest- disallows-barewords - (is (thrown? Exception (read-json " foo ")))) - -(deftest- disallows-unclosed-arrays - (is (thrown? Exception (read-json "[1, 2, ")))) - -(deftest- disallows-unclosed-objects - (is (thrown? Exception (read-json "{\"a\":1, ")))) - -(deftest- can-get-keyword-keys - (is (= {:a [1 2 {:b [3 "four"]} 5.5]} - (binding [*json-keyword-keys* true] - (read-json "{\"a\":[1,2,{\"b\":[3,\"four\"]},5.5]}"))))) - -(declare *pass1-string*) - -(deftest- pass1-test - (let [input (read-json *pass1-string*)] - (is (= "JSON Test Pattern pass1" (first input))) - (is (= "array with 1 element" (get-in input [1 "object with 1 member" 0]))) - (is (= 1234567890 (get-in input [8 "integer"]))) - (is (= "rosebud" (last input))))) - -; from http://www.json.org/JSON_checker/test/pass1.json -(def *pass1-string* - "[ - \"JSON Test Pattern pass1\", - {\"object with 1 member\":[\"array with 1 element\"]}, - {}, - [], - -42, - true, - false, - null, - { - \"integer\": 1234567890, - \"real\": -9876.543210, - \"e\": 0.123456789e-12, - \"E\": 1.234567890E+34, - \"\": 23456789012E66, - \"zero\": 0, - \"one\": 1, - \"space\": \" \", - \"quote\": \"\\\"\", - \"backslash\": \"\\\\\", - \"controls\": \"\\b\\f\\n\\r\\t\", - \"slash\": \"/ & \\/\", - \"alpha\": \"abcdefghijklmnopqrstuvwyz\", - \"ALPHA\": \"ABCDEFGHIJKLMNOPQRSTUVWYZ\", - \"digit\": \"0123456789\", - \"0123456789\": \"digit\", - \"special\": \"`1~!@#$%^&*()_+-={':[,]}|;.</>?\", - \"hex\": \"\\u0123\\u4567\\u89AB\\uCDEF\\uabcd\\uef4A\", - \"true\": true, - \"false\": false, - \"null\": null, - \"array\":[ ], - \"object\":{ }, - \"address\": \"50 St. James Street\", - \"url\": \"http://www.JSON.org/\", - \"comment\": \"// /* <!-- --\", - \"# -- --> */\": \" \", - \" s p a c e d \" :[1,2 , 3 - -, - -4 , 5 , 6 ,7 ],\"compact\":[1,2,3,4,5,6,7], - \"jsontext\": \"{\\\"object with 1 member\\\":[\\\"array with 1 element\\\"]}\", - \"quotes\": \"" \\u0022 %22 0x22 034 "\", - \"\\/\\\\\\\"\\uCAFE\\uBABE\\uAB98\\uFCDE\\ubcda\\uef4A\\b\\f\\n\\r\\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?\" -: \"A key can be any string\" - }, - 0.5 ,98.6 -, -99.44 -, - -1066, -1e1, -0.1e1, -1e-1, -1e00,2e+00,2e-00 -,\"rosebud\"]")
\ No newline at end of file diff --git a/src/main/clojure/clojure/contrib/json/write.clj b/src/main/clojure/clojure/contrib/json/write.clj deleted file mode 100644 index 5f56f211..00000000 --- a/src/main/clojure/clojure/contrib/json/write.clj +++ /dev/null @@ -1,193 +0,0 @@ -;;; json/write.clj: JavaScript Object Notation (JSON) generator - -;; by Stuart Sierra, http://stuartsierra.com/ -;; May 9, 2009 - -;; Copyright (c) Stuart Sierra, 2009. 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 - #^{:author "Stuart Sierra", - :doc "JavaScript Object Notation (JSON) generator. - -This library will generate JSON from the following types: - * nil - * all primitives (Boolean, Byte, Short, Integer, Long, Float, Double) - * String (actually any CharSequence) - * java.util.Map (including Clojure maps) - * java.util.Collection (including Clojure vectors, lists, and sets) - * Java arrays - -You can extend this library to handle new types by adding methods to -print-json. - -This library does NOT attempt to preserve round-trip equality between -JSON and Clojure data types. That is, if you write a JSON string with -this library, then read it back with clojure.contrib.json.read, you -won't necessarily get the exact same data structure. For example, -Clojure sets are written as JSON arrays, which will be read back as -Clojure vectors. - -If you want indented output, try the clojure-json library at -http://github.com/danlarkin/clojure-json - -This implementation attempts to follow the description of JSON at -<http://json.org/>. Maps become JSON objects, all other collections -become JSON arrays. JSON object keys are always converted to strings. -Within strings, all non-ASCII characters are hexadecimal escaped. -", - :see-also [["http://json.org/", "JSON Home Page"]]} - clojure.contrib.json.write - (:require [clojure.contrib.java-utils :as j]) - (:use [clojure.test :only (deftest- is)])) - -(defmulti - #^{:doc "Prints x as JSON. Nil becomes JSON null. Keywords become - strings, without the leading colon. Maps become JSON objects, all - other collection types become JSON arrays. Java arrays become JSON - arrays. Unicode characters in strings are escaped as \\uXXXX. - Numbers print as with pr." - :arglists '([x])} - print-json (fn [x] (cond - (nil? x) nil ;; prevent NullPointerException on next line - (.isArray (class x)) ::array - :else (type x)))) - - -;; Primitive types can be printed with Clojure's pr function. -(derive java.lang.Boolean ::pr) -(derive java.lang.Byte ::pr) -(derive java.lang.Short ::pr) -(derive java.lang.Integer ::pr) -(derive java.lang.Long ::pr) -(derive java.lang.Float ::pr) -(derive java.lang.Double ::pr) - -;; Collection types can be printed as JSON objects or arrays. -(derive java.util.Map ::object) -(derive java.util.Collection ::array) - -;; Symbols and keywords are converted to strings. -(derive clojure.lang.Symbol ::symbol) -(derive clojure.lang.Keyword ::symbol) - - -(defmethod print-json ::pr [x] (pr x)) - -(defmethod print-json nil [x] (print "null")) - -(defmethod print-json ::symbol [x] (print-json (name x))) - -(defmethod print-json ::array [s] - (print \[) - (loop [x s] - (when (seq x) - (let [fst (first x) - nxt (next x)] - (print-json fst) - (when (seq nxt) - (print \,) - (recur nxt))))) - (print \])) - -(defmethod print-json ::object [m] - (print \{) - (loop [x m] - (when (seq m) - (let [[k v] (first x)] - (when (nil? k) - (throw (Exception. "JSON object keys cannot be nil/null"))) - (print-json (j/as-str k)) - (print \:) - (print-json v)) - (let [nxt (next x)] - (when (seq nxt) - (print \,) - (recur nxt))))) - (print \})) - -(defmethod print-json java.lang.CharSequence [#^CharSequence s] - (let [sb (StringBuilder. (count s))] - (.append sb \") - (dotimes [i (count s)] - (let [cp (Character/codePointAt s i)] - (cond - ;; Handle printable JSON escapes before ASCII - (= cp 34) (.append sb "\\\"") - (= cp 92) (.append sb "\\\\") - (= cp 47) (.append sb "\\/") - ;; Print simple ASCII characters - (< 31 cp 127) (.append sb (.charAt s i)) - ;; Handle non-printable JSON escapes - (= cp 8) (.append sb "\\b") - (= cp 12) (.append sb "\\f") - (= cp 10) (.append sb "\\n") - (= cp 13) (.append sb "\\r") - (= cp 9) (.append sb "\\t") - ;; Any other character is Hexadecimal-escaped - :else (.append sb (format "\\u%04x" cp))))) - (.append sb \") - (print (str sb)))) - -(defn json-str - "Converts x to a JSON-formatted string." - [x] - (with-out-str (print-json x))) - - - -;;; TESTS - -;; Run these tests with -;; (clojure.test/run-tests 'clojure.contrib.print-json) - -;; Bind clojure.test/*load-tests* to false to omit these -;; tests from production code. - -(deftest- can-print-json-strings - (is (= "\"Hello, World!\"" (json-str "Hello, World!"))) - (is (= "\"\\\"Embedded\\\" Quotes\"" (json-str "\"Embedded\" Quotes")))) - -(deftest- can-print-unicode - (is (= "\"\\u1234\\u4567\"" (json-str "\u1234\u4567")))) - -(deftest- can-print-json-null - (is (= "null" (json-str nil)))) - -(deftest- can-print-json-arrays - (is (= "[1,2,3]" (json-str [1 2 3]))) - (is (= "[1,2,3]" (json-str (list 1 2 3)))) - (is (= "[1,2,3]" (json-str (sorted-set 1 2 3)))) - (is (= "[1,2,3]" (json-str (seq [1 2 3]))))) - -(deftest- can-print-java-arrays - (is (= "[1,2,3]" (json-str (into-array [1 2 3]))))) - -(deftest- can-print-empty-arrays - (is (= "[]" (json-str []))) - (is (= "[]" (json-str (list)))) - (is (= "[]" (json-str #{})))) - -(deftest- can-print-json-objects - (is (= "{\"a\":1,\"b\":2}" (json-str (sorted-map :a 1 :b 2))))) - -(deftest- object-keys-must-be-strings - (is (= "{\"1\":1,\"2\":2") (json-str (sorted-map 1 1 2 2)))) - -(deftest- can-print-empty-objects - (is (= "{}" (json-str {})))) - -(deftest- accept-sequence-of-nils - (is (= "[null,null,null]" (json-str [nil nil nil])))) - -(deftest- error-on-nil-keys - (is (thrown? Exception (json-str {nil 1})))) - -(deftest- characters-in-symbols-are-escaped - (is (= "\"foo\\u1b1b\"" (json-str (symbol "foo\u1b1b")))))
\ No newline at end of file diff --git a/src/main/clojure/clojure/contrib/str_utils.clj b/src/main/clojure/clojure/contrib/str_utils.clj deleted file mode 100644 index 02bf7445..00000000 --- a/src/main/clojure/clojure/contrib/str_utils.clj +++ /dev/null @@ -1,100 +0,0 @@ -;;; str_utils.clj -- string utilities for Clojure - -;; by Stuart Sierra <mail@stuartsierra.com> -;; April 8, 2008 - -;; Copyright (c) Stuart Sierra, 2008. 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 - #^{:author "Stuart Sierra", - :doc "String utilities for Clojure"} - clojure.contrib.str-utils - (:import (java.util.regex Pattern))) - -(defn re-split - "Splits the string on instances of 'pattern'. Returns a sequence of - strings. Optional 'limit' argument is the maximum number of - splits. Like Perl's 'split'." - ([#^Pattern pattern string] (seq (. pattern (split string)))) - ([#^Pattern pattern string limit] (seq (. pattern (split string limit))))) - -(defn re-partition - "Splits the string into a lazy sequence of substrings, alternating - between substrings that match the patthern and the substrings - between the matches. The sequence always starts with the substring - before the first match, or an empty string if the beginning of the - string matches. - - For example: (re-partition #\"[a-z]+\" \"abc123def\") - - Returns: (\"\" \"abc\" \"123\" \"def\")" - [#^Pattern re string] - (let [m (re-matcher re string)] - ((fn step [prevend] - (lazy-seq - (if (.find m) - (cons (.subSequence string prevend (.start m)) - (cons (re-groups m) - (step (+ (.start m) (count (.group m)))))) - (when (< prevend (.length string)) - (list (.subSequence string prevend (.length string))))))) - 0))) - -(defn re-gsub - "Replaces all instances of 'pattern' in 'string' with - 'replacement'. Like Ruby's 'String#gsub'. - - If (ifn? replacment) is true, the replacement is called with the - match. - " - [#^java.util.regex.Pattern regex replacement #^String string] - (if (ifn? replacement) - (let [parts (vec (re-partition regex string))] - (apply str - (reduce (fn [parts match-idx] - (update-in parts [match-idx] replacement)) - parts (range 1 (count parts) 2)))) - (.. regex (matcher string) (replaceAll replacement)))) - -(defn re-sub - "Replaces the first instance of 'pattern' in 'string' with - 'replacement'. Like Ruby's 'String#sub'. - - If (ifn? replacement) is true, the replacement is called with - the match. - " - [#^Pattern regex replacement #^String string] - (if (ifn? replacement) - (let [m (re-matcher regex string)] - (if (.find m) - (str (.subSequence string 0 (.start m)) - (replacement (re-groups m)) - (.subSequence string (.end m) (.length string))) - string)) - (.. regex (matcher string) (replaceFirst replacement)))) - - -(defn str-join - "Returns a string of all elements in 'sequence', separated by - 'separator'. Like Perl's 'join'." - [separator sequence] - (apply str (interpose separator sequence))) - - -(defn chop - "Removes the last character of string." - [s] - (subs s 0 (dec (count s)))) - -(defn chomp - "Removes all trailing newline \\n or return \\r characters from - string. Note: String.trim() is similar and faster." - [s] - (re-sub #"[\r\n]+$" "" s)) diff --git a/src/main/clojure/clojure/contrib/str_utils2.clj b/src/main/clojure/clojure/contrib/str_utils2.clj deleted file mode 100644 index d49351ac..00000000 --- a/src/main/clojure/clojure/contrib/str_utils2.clj +++ /dev/null @@ -1,373 +0,0 @@ -;;; str_utils2.clj -- functional string utilities for Clojure - -;; by Stuart Sierra, http://stuartsierra.com/ -;; August 19, 2009 - -;; Copyright (c) Stuart Sierra, 2009. 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 #^{:author "Stuart Sierra" - :doc "This is a library of string manipulation functions. It - is intented as a replacement for clojure.contrib.str-utils. - - You cannot (use 'clojure.contrib.str-utils2) because it defines - functions with the same names as functions in clojure.core. - Instead, do (require '[clojure.contrib.str-utils2 :as s]) - or something similar. - - Goals: - 1. Be functional - 2. String argument first, to work with -> - 3. Performance linear in string length - - Some ideas are borrowed from - http://github.com/francoisdevlin/devlinsf-clojure-utils/"} - clojure.contrib.str-utils2 - (:refer-clojure :exclude (take replace drop butlast partition - contains? get repeat reverse partial)) - (:import (java.util.regex Pattern))) - - -(defmacro dochars - "bindings => [name string] - - Repeatedly executes body, with name bound to each character in - string. Does NOT handle Unicode supplementary characters (above - U+FFFF)." - [bindings & body] - (assert (vector bindings)) - (assert (= 2 (count bindings))) - ;; This seems to be the fastest way to iterate over characters. - `(let [#^String s# ~(second bindings)] - (dotimes [i# (.length s#)] - (let [~(first bindings) (.charAt s# i#)] - ~@body)))) - - -(defmacro docodepoints - "bindings => [name string] - - Repeatedly executes body, with name bound to the integer code point - of each Unicode character in the string. Handles Unicode - supplementary characters (above U+FFFF) correctly." - [bindings & body] - (assert (vector bindings)) - (assert (= 2 (count bindings))) - (let [character (first bindings) - string (second bindings)] - `(let [#^String s# ~string - len# (.length s#)] - (loop [i# 0] - (when (< i# len#) - (let [~character (.charAt s# i#)] - (if (Character/isHighSurrogate ~character) - (let [~character (.codePointAt s# i#)] - ~@body - (recur (+ 2 i#))) - (let [~character (int ~character)] - ~@body - (recur (inc i#)))))))))) - -(defn codepoints - "Returns a sequence of integer Unicode code points in s. Handles - Unicode supplementary characters (above U+FFFF) correctly." - [#^String s] - (let [len (.length s) - f (fn thisfn [#^String s i] - (when (< i len) - (let [c (.charAt s i)] - (if (Character/isHighSurrogate c) - (cons (.codePointAt s i) (thisfn s (+ 2 i))) - (cons (int c) (thisfn s (inc i)))))))] - (lazy-seq (f s 0)))) - -(defn #^String escape - "Returns a new String by applying cmap (a function or a map) to each - character in s. If cmap returns nil, the original character is - added to the output unchanged." - [#^String s cmap] - (let [buffer (StringBuilder. (.length s))] - (dochars [c s] - (if-let [r (cmap c)] - (.append buffer r) - (.append buffer c))) - (.toString buffer))) - -(defn blank? - "True if s is nil, empty, or contains only whitespace." - [#^String s] - (every? (fn [#^Character c] (Character/isWhitespace c)) s)) - -(defn #^String take - "Take first n characters from s, up to the length of s. - - Note the argument order is the opposite of clojure.core/take; this - is to keep the string as the first argument for use with ->" - [#^String s n] - (if (< (count s) n) - s - (.substring s 0 n))) - -(defn #^String drop - "Drops first n characters from s. Returns an empty string if n is - greater than the length of s. - - Note the argument order is the opposite of clojure.core/drop; this - is to keep the string as the first argument for use with ->" - [#^String s n] - (if (< (count s) n) - "" - (.substring s n))) - -(defn #^String butlast - "Returns s without the last n characters. Returns an empty string - if n is greater than the length of s. - - Note the argument order is the opposite of clojure.core/butlast; - this is to keep the string as the first argument for use with ->" - [#^String s n] - (if (< (count s) n) - "" - (.substring s 0 (- (count s) n)))) - -(defn #^String tail - "Returns the last n characters of s." - [#^String s n] - (if (< (count s) n) - s - (.substring s (- (count s) n)))) - -(defn #^String repeat - "Returns a new String containing s repeated n times." - [#^String s n] - (apply str (clojure.core/repeat n s))) - -(defn #^String reverse - "Returns s with its characters reversed." - [#^String s] - (.toString (.reverse (StringBuilder. s)))) - -(defmulti - #^{:doc "Replaces all instances of pattern in string with replacement. - - Allowed argument types for pattern and replacement are: - 1. String and String - 2. Character and Character - 3. regex Pattern and String - (Uses java.util.regex.Matcher.replaceAll) - 4. regex Pattern and function - (Calls function with re-groups of each match, uses return - value as replacement.)" - :arglists '([string pattern replacement]) - :tag String} - replace - (fn [#^String string pattern replacement] - [(class pattern) (class replacement)])) - -(defmethod replace [String String] [#^String s #^String a #^String b] - (.replace s a b)) - -(defmethod replace [Character Character] [#^String s #^Character a #^Character b] - (.replace s a b)) - -(defmethod replace [Pattern String] [#^String s re replacement] - (.replaceAll (re-matcher re s) replacement)) - -(defmethod replace [Pattern clojure.lang.IFn] [#^String s re replacement] - (let [m (re-matcher re s)] - (let [buffer (StringBuffer. (.length s))] - (loop [] - (if (.find m) - (do (.appendReplacement m buffer (replacement (re-groups m))) - (recur)) - (do (.appendTail m buffer) - (.toString buffer))))))) - -(defmulti - #^{:doc "Replaces the first instance of pattern in s with replacement. - - Allowed argument types for pattern and replacement are: - 1. String and String - 2. regex Pattern and String - (Uses java.util.regex.Matcher.replaceAll) - 3. regex Pattern and function -" - :arglists '([s pattern replacement]) - :tag String} - replace-first - (fn [s pattern replacement] - [(class pattern) (class replacement)])) - -(defmethod replace-first [String String] [#^String s pattern replacement] - (.replaceFirst (re-matcher (Pattern/quote pattern) s) replacement)) - -(defmethod replace-first [Pattern String] [#^String s re replacement] - (.replaceFirst (re-matcher re s) replacement)) - -(defmethod replace-first [Pattern clojure.lang.IFn] [#^String s #^Pattern re f] - (let [m (re-matcher re s)] - (let [buffer (StringBuffer.)] - (if (.find m) - (let [rep (f (re-groups m))] - (.appendReplacement m buffer rep) - (.appendTail m buffer) - (str buffer)))))) - -(defn partition - "Splits the string into a lazy sequence of substrings, alternating - between substrings that match the patthern and the substrings - between the matches. The sequence always starts with the substring - before the first match, or an empty string if the beginning of the - string matches. - - For example: (partition \"abc123def\" #\"[a-z]+\") - returns: (\"\" \"abc\" \"123\" \"def\")" - [#^String s #^Pattern re] - (let [m (re-matcher re s)] - ((fn step [prevend] - (lazy-seq - (if (.find m) - (cons (.subSequence s prevend (.start m)) - (cons (re-groups m) - (step (+ (.start m) (count (.group m)))))) - (when (< prevend (.length s)) - (list (.subSequence s prevend (.length s))))))) - 0))) - -(defn #^String join - "Returns a string of all elements in coll, separated by - separator. Like Perl's join." - [#^String separator coll] - (apply str (interpose separator coll))) - -(defn #^String chop - "Removes the last character of string, does nothing on a zero-length - string." - [#^String s] - (let [size (count s)] - (if (zero? size) - s - (subs s 0 (dec (count s)))))) - -(defn #^String chomp - "Removes all trailing newline \\n or return \\r characters from - string. Note: String.trim() is similar and faster." - [#^String s] - (replace s #"[\r\n]+$" "")) - -(defn title-case [#^String s] - (throw (Exception. "title-case not implemeted yet"))) - -(defn #^String swap-case - "Changes upper case characters to lower case and vice-versa. - Handles Unicode supplementary characters correctly. Uses the - locale-sensitive String.toUpperCase() and String.toLowerCase() - methods." - [#^String s] - (let [buffer (StringBuilder. (.length s)) - ;; array to make a String from one code point - #^"[I" array (make-array Integer/TYPE 1)] - (docodepoints [c s] - (aset-int array 0 c) - (if (Character/isLowerCase c) - ;; Character.toUpperCase is not locale-sensitive, but - ;; String.toUpperCase is; so we use a String. - (.append buffer (.toUpperCase (String. array 0 1))) - (.append buffer (.toLowerCase (String. array 0 1))))) - (.toString buffer))) - -(defn #^String capitalize - "Converts first character of the string to upper-case, all other - characters to lower-case." - [#^String s] - (if (< (count s) 2) - (.toUpperCase s) - (str (.toUpperCase #^String (subs s 0 1)) - (.toLowerCase #^String (subs s 1))))) - -(defn #^String ltrim - "Removes whitespace from the left side of string." - [#^String s] - (replace s #"^\s+" "")) - -(defn #^String rtrim - "Removes whitespace from the right side of string." - [#^String s] - (replace s #"\s+$" "")) - -(defn split-lines - "Splits s on \\n or \\r\\n." - [#^String s] - (seq (.split #"\r?\n" s))) - -;; borrowed from compojure.str-utils, by James Reeves, EPL 1.0 -(defn #^String map-str - "Apply f to each element of coll, concatenate all results into a - String." - [f coll] - (apply str (map f coll))) - -;; borrowed from compojure.str-utils, by James Reeves, EPL 1.0 -(defn grep - "Filters elements of coll by a regular expression. The String - representation (with str) of each element is tested with re-find." - [re coll] - (filter (fn [x] (re-find re (str x))) coll)) - -(defn partial - "Like clojure.core/partial for functions that take their primary - argument first. - - Takes a function f and its arguments, NOT INCLUDING the first - argument. Returns a new function whose first argument will be the - first argument to f. - - Example: (str-utils2/partial str-utils2/take 2) - ;;=> (fn [s] (str-utils2/take s 2))" - [f & args] - (fn [s & more] (apply f s (concat args more)))) - - -;;; WRAPPERS - -;; The following functions are simple wrappers around java.lang.String -;; functions. They are included here for completeness, and for use -;; when mapping over a collection of strings. - -(defn #^String upper-case - "Converts string to all upper-case." - [#^String s] - (.toUpperCase s)) - -(defn #^String lower-case - "Converts string to all lower-case." - [#^String s] - (.toLowerCase s)) - -(defn split - "Splits string on a regular expression. Optional argument limit is - the maximum number of splits." - ([#^String s #^Pattern re] (seq (.split re s))) - ([#^String s #^Pattern re limit] (seq (.split re s limit)))) - -(defn #^String trim - "Removes whitespace from both ends of string." - [#^String s] - (.trim s)) - -(defn #^String contains? - "True if s contains the substring." - [#^String s substring] - (.contains s substring)) - -(defn #^String get - "Gets the i'th character in string." - [#^String s i] - (.charAt s i)) - |