diff options
Diffstat (limited to 'src/clojure/contrib/duck_streams.clj')
-rw-r--r-- | src/clojure/contrib/duck_streams.clj | 119 |
1 files changed, 119 insertions, 0 deletions
diff --git a/src/clojure/contrib/duck_streams.clj b/src/clojure/contrib/duck_streams.clj new file mode 100644 index 00000000..a1936676 --- /dev/null +++ b/src/clojure/contrib/duck_streams.clj @@ -0,0 +1,119 @@ +;;; duck_streams.clj -- duck-typed I/O streams for Clojure + +;; by Stuart Sierra <mail@stuartsierra.com> +;; April 8, 2008 + +;; Copyright (c) 2008 Stuart Sierra. All rights reserved. The use and +;; distribution terms for this software are covered by the Common +;; Public License 1.0 (http://www.opensource.org/licenses/cpl1.0.php) +;; which can be found in the file CPL.TXT 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. + + +;; This file defines "duck-typed" I/O utility functions for Clojure. +;; The 'reader' and 'writer' functions will open and return an +;; instance of java.io.BufferedReader and java.io.PrintWriter, +;; respectively, for a variety of argument types -- filenames as +;; strings, URLs, java.io.File's, etc. These functions are not very +;; efficient, because they have to perform a number of 'instance?' +;; checks, but they are convenient when you just want to open a file +;; and don't want to deal with all the Java I/O classes. +;; +;; This file also defines two convenience functions, 'spit' (opposite +;; of 'slurp') and 'write-lines' (opposite of 'line-seq'). + + +(ns clojure.contrib.duck-streams + (:import + (java.io Reader InputStream InputStreamReader FileReader + BufferedReader File PrintWriter OutputStream + OutputStreamWriter BufferedWriter Writer FileWriter) + (java.net URI URL MalformedURLException))) + +(defmacro bufr + {:private true} + [reader] + `(new java.io.BufferedReader ~reader)) + +(defn reader + "Attempts to coerce its argument into an open + java.io.BufferedReader. Argument may be an instance of Reader, + BufferedReader, InputStream, File, URI, URL, or String. + + If argument is a String, it tries to resolve it first as a URI, then + as a local file name. URIs with a 'file' protocol are converted to + local file names. + + Should be used inside with-open to ensure the Reader is properly + closed." + [x] + (cond + (instance? BufferedReader x) x + (instance? Reader x) (bufr x) + (instance? InputStream x) (bufr (new InputStreamReader x)) + (instance? File x) (bufr (new FileReader #^File x)) + (instance? URL x) (if (= (. #^URL x (getProtocol)) "file") + (bufr (new FileReader (. #^URL x (getPath)))) + (bufr (new InputStreamReader (. #^URL x (openStream))))) + (instance? URI x) (reader (. #^URI x (toURL))) + (instance? String x) (try (let [url (new URL x)] + (reader url)) + (catch MalformedURLException err + (bufr (new FileReader #^String x)))) + :else (throw (new Exception (str "Cannot coerce " (class x) + " into a Reader."))))) + +(defmacro bufw + {:private true} + [writer] + `(new java.io.PrintWriter (new java.io.BufferedWriter ~writer))) + +(defn writer + "Attempts to coerce its argument into an open java.io.PrintWriter + wrapped around a java.io.BufferedWriter. Argument may be an + instance of Writer, PrintWriter, BufferedWriter, OutputStream, File, + URI, URL, or String. + + If argument is a String, it tries to resolve it first as a URI, then + as a local file name. URIs with a 'file' protocol are converted to + local file names. + + Should be used inside with-open to ensure the Writer is properly + closed." + [x] + (cond + (instance? PrintWriter x) x + (instance? BufferedWriter x) (new PrintWriter #^BufferedWriter x) + (instance? Writer x) (bufw x) ; includes FileWriter + (instance? OutputStream x) (bufw (new OutputStreamWriter x)) + (instance? File x) (bufw (new FileWriter #^File x)) + (instance? URL x) (if (= (. #^URL x (getProtocol)) "file") + (bufw (new FileWriter (. #^URL x (getPath)))) + (throw (new Exception (str "Cannot write to non-file URL <" x ">.")))) + (instance? URI x) (writer (. #^URI x (toURL))) + (instance? String x) (try (let [url (new URL x)] + (writer url)) + (catch MalformedURLException err + (bufw (new FileWriter #^String x)))) + :else (throw (new Exception (str "Cannot coerce " (class x) + " into a Writer."))))) + +(defn write-lines + "Opposite of 'line-seq'. Writes lines (a seq) to writer (an open + java.io.PrintWriter), separated by newlines." + [#^PrintWriter writer lines] + (let [line (first lines)] + (when line + (. writer (write (str line))) + (. writer (println)) + (recur writer (rest lines))))) + +(defn spit + "Opposite of 'slurp'. Writes 'contents' to the file named by + 'filename'." + [filename contents] + (with-open [w (#^PrintWriter writer filename)] + (. w (print contents)))) + |