Mercurial > lasercutter
diff src/clojure/java/io.clj @ 10:ef7dbbd6452c
added clojure source goodness
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sat, 21 Aug 2010 06:25:44 -0400 |
parents | |
children |
line wrap: on
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/clojure/java/io.clj Sat Aug 21 06:25:44 2010 -0400 1.3 @@ -0,0 +1,427 @@ 1.4 +; Copyright (c) Rich Hickey. All rights reserved. 1.5 +; The use and distribution terms for this software are covered by the 1.6 +; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 1.7 +; which can be found in the file epl-v10.html at the root of this distribution. 1.8 +; By using this software in any fashion, you are agreeing to be bound by 1.9 +; the terms of this license. 1.10 +; You must not remove this notice, or any other, from this software. 1.11 + 1.12 +(ns 1.13 + ^{:author "Stuart Sierra, Chas Emerick, Stuart Halloway", 1.14 + :doc "This file defines polymorphic I/O utility functions for Clojure."} 1.15 + clojure.java.io 1.16 + (:import 1.17 + (java.io Reader InputStream InputStreamReader PushbackReader 1.18 + BufferedReader File OutputStream 1.19 + OutputStreamWriter BufferedWriter Writer 1.20 + FileInputStream FileOutputStream ByteArrayOutputStream 1.21 + StringReader ByteArrayInputStream 1.22 + BufferedInputStream BufferedOutputStream 1.23 + CharArrayReader Closeable) 1.24 + (java.net URI URL MalformedURLException Socket))) 1.25 + 1.26 +(def 1.27 + ^{:doc "Type object for a Java primitive byte array." 1.28 + :private true 1.29 + } 1.30 + byte-array-type (class (make-array Byte/TYPE 0))) 1.31 + 1.32 +(def 1.33 + ^{:doc "Type object for a Java primitive char array." 1.34 + :private true} 1.35 + char-array-type (class (make-array Character/TYPE 0))) 1.36 + 1.37 +(defprotocol ^{:added "1.2"} Coercions 1.38 + "Coerce between various 'resource-namish' things." 1.39 + (^{:tag java.io.File, :added "1.2"} as-file [x] "Coerce argument to a file.") 1.40 + (^{:tag java.net.URL, :added "1.2"} as-url [x] "Coerce argument to a URL.")) 1.41 + 1.42 +(extend-protocol Coercions 1.43 + nil 1.44 + (as-file [_] nil) 1.45 + (as-url [_] nil) 1.46 + 1.47 + String 1.48 + (as-file [s] (File. s)) 1.49 + (as-url [s] (URL. s)) 1.50 + 1.51 + File 1.52 + (as-file [f] f) 1.53 + (as-url [f] (.toURL f)) 1.54 + 1.55 + URL 1.56 + (as-url [u] u) 1.57 + (as-file [u] 1.58 + (if (= "file" (.getProtocol u)) 1.59 + (as-file (.getPath u)) 1.60 + (throw (IllegalArgumentException. "Not a file: " u)))) 1.61 + 1.62 + URI 1.63 + (as-url [u] (.toURL u)) 1.64 + (as-file [u] (as-file (as-url u)))) 1.65 + 1.66 +(defprotocol ^{:added "1.2"} IOFactory 1.67 + "Factory functions that create ready-to-use, buffered versions of 1.68 + the various Java I/O stream types, on top of anything that can 1.69 + be unequivocally converted to the requested kind of stream. 1.70 + 1.71 + Common options include 1.72 + 1.73 + :append true to open stream in append mode 1.74 + :encoding string name of encoding to use, e.g. \"UTF-8\". 1.75 + 1.76 + Callers should generally prefer the higher level API provided by 1.77 + reader, writer, input-stream, and output-stream." 1.78 + (^{:added "1.2"} make-reader [x opts] "Creates a BufferedReader. See also IOFactory docs.") 1.79 + (^{:added "1.2"} make-writer [x opts] "Creates a BufferedWriter. See also IOFactory docs.") 1.80 + (^{:added "1.2"} make-input-stream [x opts] "Creates a BufferedInputStream. See also IOFactory docs.") 1.81 + (^{:added "1.2"} make-output-stream [x opts] "Creates a BufferedOutputStream. See also IOFactory docs.")) 1.82 + 1.83 +(defn ^Reader reader 1.84 + "Attempts to coerce its argument into an open java.io.Reader. 1.85 + Default implementations always return a java.io.BufferedReader. 1.86 + 1.87 + Default implementations are provided for Reader, BufferedReader, 1.88 + InputStream, File, URI, URL, Socket, byte arrays, character arrays, 1.89 + and String. 1.90 + 1.91 + If argument is a String, it tries to resolve it first as a URI, then 1.92 + as a local file name. URIs with a 'file' protocol are converted to 1.93 + local file names. 1.94 + 1.95 + Should be used inside with-open to ensure the Reader is properly 1.96 + closed." 1.97 + {:added "1.2"} 1.98 + [x & opts] 1.99 + (make-reader x (when opts (apply hash-map opts)))) 1.100 + 1.101 +(defn ^Writer writer 1.102 + "Attempts to coerce its argument into an open java.io.Writer. 1.103 + Default implementations always return a java.io.BufferedWriter. 1.104 + 1.105 + Default implementations are provided for Writer, BufferedWriter, 1.106 + OutputStream, File, URI, URL, Socket, and String. 1.107 + 1.108 + If the argument is a String, it tries to resolve it first as a URI, then 1.109 + as a local file name. URIs with a 'file' protocol are converted to 1.110 + local file names. 1.111 + 1.112 + Should be used inside with-open to ensure the Writer is properly 1.113 + closed." 1.114 + {:added "1.2"} 1.115 + [x & opts] 1.116 + (make-writer x (when opts (apply hash-map opts)))) 1.117 + 1.118 +(defn ^InputStream input-stream 1.119 + "Attempts to coerce its argument into an open java.io.InputStream. 1.120 + Default implementations always return a java.io.BufferedInputStream. 1.121 + 1.122 + Default implementations are defined for OutputStream, File, URI, URL, 1.123 + Socket, byte array, and String arguments. 1.124 + 1.125 + If the argument is a String, it tries to resolve it first as a URI, then 1.126 + as a local file name. URIs with a 'file' protocol are converted to 1.127 + local file names. 1.128 + 1.129 + Should be used inside with-open to ensure the InputStream is properly 1.130 + closed." 1.131 + {:added "1.2"} 1.132 + [x & opts] 1.133 + (make-input-stream x (when opts (apply hash-map opts)))) 1.134 + 1.135 +(defn ^OutputStream output-stream 1.136 + "Attempts to coerce its argument into an open java.io.OutputStream. 1.137 + Default implementations always return a java.io.BufferedOutputStream. 1.138 + 1.139 + Default implementations are defined for OutputStream, File, URI, URL, 1.140 + Socket, and String arguments. 1.141 + 1.142 + If the argument is a String, it tries to resolve it first as a URI, then 1.143 + as a local file name. URIs with a 'file' protocol are converted to 1.144 + local file names. 1.145 + 1.146 + Should be used inside with-open to ensure the OutputStream is 1.147 + properly closed." 1.148 + {:added "1.2"} 1.149 + [x & opts] 1.150 + (make-output-stream x (when opts (apply hash-map opts)))) 1.151 + 1.152 +(defn- ^Boolean append? [opts] 1.153 + (boolean (:append opts))) 1.154 + 1.155 +(defn- ^String encoding [opts] 1.156 + (or (:encoding opts) "UTF-8")) 1.157 + 1.158 +(defn- buffer-size [opts] 1.159 + (or (:buffer-size opts) 1024)) 1.160 + 1.161 +(def default-streams-impl 1.162 + {:make-reader (fn [x opts] (make-reader (make-input-stream x opts) opts)) 1.163 + :make-writer (fn [x opts] (make-writer (make-output-stream x opts) opts)) 1.164 + :make-input-stream (fn [x opts] 1.165 + (throw (IllegalArgumentException. 1.166 + (str "Cannot open <" (pr-str x) "> as an InputStream.")))) 1.167 + :make-output-stream (fn [x opts] 1.168 + (throw (IllegalArgumentException. 1.169 + (str "Cannot open <" (pr-str x) "> as an OutputStream."))))}) 1.170 + 1.171 +(defn- inputstream->reader 1.172 + [^InputStream is opts] 1.173 + (make-reader (InputStreamReader. is (encoding opts)) opts)) 1.174 + 1.175 +(defn- outputstream->writer 1.176 + [^OutputStream os opts] 1.177 + (make-writer (OutputStreamWriter. os (encoding opts)) opts)) 1.178 + 1.179 +(extend BufferedInputStream 1.180 + IOFactory 1.181 + (assoc default-streams-impl 1.182 + :make-input-stream (fn [x opts] x) 1.183 + :make-reader inputstream->reader)) 1.184 + 1.185 +(extend InputStream 1.186 + IOFactory 1.187 + (assoc default-streams-impl 1.188 + :make-input-stream (fn [x opts] (BufferedInputStream. x)) 1.189 + :make-reader inputstream->reader)) 1.190 + 1.191 +(extend Reader 1.192 + IOFactory 1.193 + (assoc default-streams-impl 1.194 + :make-reader (fn [x opts] (BufferedReader. x)))) 1.195 + 1.196 +(extend BufferedReader 1.197 + IOFactory 1.198 + (assoc default-streams-impl 1.199 + :make-reader (fn [x opts] x))) 1.200 + 1.201 +(extend Writer 1.202 + IOFactory 1.203 + (assoc default-streams-impl 1.204 + :make-writer (fn [x opts] (BufferedWriter. x)))) 1.205 + 1.206 +(extend BufferedWriter 1.207 + IOFactory 1.208 + (assoc default-streams-impl 1.209 + :make-writer (fn [x opts] x))) 1.210 + 1.211 +(extend OutputStream 1.212 + IOFactory 1.213 + (assoc default-streams-impl 1.214 + :make-output-stream (fn [x opts] (BufferedOutputStream. x)) 1.215 + :make-writer outputstream->writer)) 1.216 + 1.217 +(extend BufferedOutputStream 1.218 + IOFactory 1.219 + (assoc default-streams-impl 1.220 + :make-output-stream (fn [x opts] x) 1.221 + :make-writer outputstream->writer)) 1.222 + 1.223 +(extend File 1.224 + IOFactory 1.225 + (assoc default-streams-impl 1.226 + :make-input-stream (fn [^File x opts] (make-input-stream (FileInputStream. x) opts)) 1.227 + :make-output-stream (fn [^File x opts] (make-output-stream (FileOutputStream. x (append? opts)) opts)))) 1.228 + 1.229 +(extend URL 1.230 + IOFactory 1.231 + (assoc default-streams-impl 1.232 + :make-input-stream (fn [^URL x opts] 1.233 + (make-input-stream 1.234 + (if (= "file" (.getProtocol x)) 1.235 + (FileInputStream. (.getPath x)) 1.236 + (.openStream x)) opts)) 1.237 + :make-output-stream (fn [^URL x opts] 1.238 + (if (= "file" (.getProtocol x)) 1.239 + (make-output-stream (File. (.getPath x)) opts) 1.240 + (throw (IllegalArgumentException. (str "Can not write to non-file URL <" x ">"))))))) 1.241 + 1.242 +(extend URI 1.243 + IOFactory 1.244 + (assoc default-streams-impl 1.245 + :make-input-stream (fn [^URI x opts] (make-input-stream (.toURL x) opts)) 1.246 + :make-output-stream (fn [^URI x opts] (make-output-stream (.toURL x) opts)))) 1.247 + 1.248 +(extend String 1.249 + IOFactory 1.250 + (assoc default-streams-impl 1.251 + :make-input-stream (fn [^String x opts] 1.252 + (try 1.253 + (make-input-stream (URL. x) opts) 1.254 + (catch MalformedURLException e 1.255 + (make-input-stream (File. x) opts)))) 1.256 + :make-output-stream (fn [^String x opts] 1.257 + (try 1.258 + (make-output-stream (URL. x) opts) 1.259 + (catch MalformedURLException err 1.260 + (make-output-stream (File. x) opts)))))) 1.261 + 1.262 +(extend Socket 1.263 + IOFactory 1.264 + (assoc default-streams-impl 1.265 + :make-input-stream (fn [^Socket x opts] (make-input-stream (.getInputStream x) opts)) 1.266 + :make-output-stream (fn [^Socket x opts] (make-output-stream (.getOutputStream x) opts)))) 1.267 + 1.268 +(extend byte-array-type 1.269 + IOFactory 1.270 + (assoc default-streams-impl 1.271 + :make-input-stream (fn [x opts] (make-input-stream (ByteArrayInputStream. x) opts)))) 1.272 + 1.273 +(extend char-array-type 1.274 + IOFactory 1.275 + (assoc default-streams-impl 1.276 + :make-reader (fn [x opts] (make-reader (CharArrayReader. x) opts)))) 1.277 + 1.278 +(extend Object 1.279 + IOFactory 1.280 + default-streams-impl) 1.281 + 1.282 +(defmulti 1.283 + #^{:doc "Internal helper for copy" 1.284 + :private true 1.285 + :arglists '([input output opts])} 1.286 + do-copy 1.287 + (fn [input output opts] [(type input) (type output)])) 1.288 + 1.289 +(defmethod do-copy [InputStream OutputStream] [#^InputStream input #^OutputStream output opts] 1.290 + (let [buffer (make-array Byte/TYPE (buffer-size opts))] 1.291 + (loop [] 1.292 + (let [size (.read input buffer)] 1.293 + (when (pos? size) 1.294 + (do (.write output buffer 0 size) 1.295 + (recur))))))) 1.296 + 1.297 +(defmethod do-copy [InputStream Writer] [#^InputStream input #^Writer output opts] 1.298 + (let [#^"[B" buffer (make-array Byte/TYPE (buffer-size opts))] 1.299 + (loop [] 1.300 + (let [size (.read input buffer)] 1.301 + (when (pos? size) 1.302 + (let [chars (.toCharArray (String. buffer 0 size (encoding opts)))] 1.303 + (do (.write output chars) 1.304 + (recur)))))))) 1.305 + 1.306 +(defmethod do-copy [InputStream File] [#^InputStream input #^File output opts] 1.307 + (with-open [out (FileOutputStream. output)] 1.308 + (do-copy input out opts))) 1.309 + 1.310 +(defmethod do-copy [Reader OutputStream] [#^Reader input #^OutputStream output opts] 1.311 + (let [#^"[C" buffer (make-array Character/TYPE (buffer-size opts))] 1.312 + (loop [] 1.313 + (let [size (.read input buffer)] 1.314 + (when (pos? size) 1.315 + (let [bytes (.getBytes (String. buffer 0 size) (encoding opts))] 1.316 + (do (.write output bytes) 1.317 + (recur)))))))) 1.318 + 1.319 +(defmethod do-copy [Reader Writer] [#^Reader input #^Writer output opts] 1.320 + (let [#^"[C" buffer (make-array Character/TYPE (buffer-size opts))] 1.321 + (loop [] 1.322 + (let [size (.read input buffer)] 1.323 + (when (pos? size) 1.324 + (do (.write output buffer 0 size) 1.325 + (recur))))))) 1.326 + 1.327 +(defmethod do-copy [Reader File] [#^Reader input #^File output opts] 1.328 + (with-open [out (FileOutputStream. output)] 1.329 + (do-copy input out opts))) 1.330 + 1.331 +(defmethod do-copy [File OutputStream] [#^File input #^OutputStream output opts] 1.332 + (with-open [in (FileInputStream. input)] 1.333 + (do-copy in output opts))) 1.334 + 1.335 +(defmethod do-copy [File Writer] [#^File input #^Writer output opts] 1.336 + (with-open [in (FileInputStream. input)] 1.337 + (do-copy in output opts))) 1.338 + 1.339 +(defmethod do-copy [File File] [#^File input #^File output opts] 1.340 + (with-open [in (FileInputStream. input) 1.341 + out (FileOutputStream. output)] 1.342 + (do-copy in out opts))) 1.343 + 1.344 +(defmethod do-copy [String OutputStream] [#^String input #^OutputStream output opts] 1.345 + (do-copy (StringReader. input) output opts)) 1.346 + 1.347 +(defmethod do-copy [String Writer] [#^String input #^Writer output opts] 1.348 + (do-copy (StringReader. input) output opts)) 1.349 + 1.350 +(defmethod do-copy [String File] [#^String input #^File output opts] 1.351 + (do-copy (StringReader. input) output opts)) 1.352 + 1.353 +(defmethod do-copy [char-array-type OutputStream] [input #^OutputStream output opts] 1.354 + (do-copy (CharArrayReader. input) output opts)) 1.355 + 1.356 +(defmethod do-copy [char-array-type Writer] [input #^Writer output opts] 1.357 + (do-copy (CharArrayReader. input) output opts)) 1.358 + 1.359 +(defmethod do-copy [char-array-type File] [input #^File output opts] 1.360 + (do-copy (CharArrayReader. input) output opts)) 1.361 + 1.362 +(defmethod do-copy [byte-array-type OutputStream] [#^"[B" input #^OutputStream output opts] 1.363 + (do-copy (ByteArrayInputStream. input) output opts)) 1.364 + 1.365 +(defmethod do-copy [byte-array-type Writer] [#^"[B" input #^Writer output opts] 1.366 + (do-copy (ByteArrayInputStream. input) output opts)) 1.367 + 1.368 +(defmethod do-copy [byte-array-type File] [#^"[B" input #^Writer output opts] 1.369 + (do-copy (ByteArrayInputStream. input) output opts)) 1.370 + 1.371 +(defn copy 1.372 + "Copies input to output. Returns nil or throws IOException. 1.373 + Input may be an InputStream, Reader, File, byte[], or String. 1.374 + Output may be an OutputStream, Writer, or File. 1.375 + 1.376 + Options are key/value pairs and may be one of 1.377 + 1.378 + :buffer-size buffer size to use, default is 1024. 1.379 + :encoding encoding to use if converting between 1.380 + byte and char streams. 1.381 + 1.382 + Does not close any streams except those it opens itself 1.383 + (on a File)." 1.384 + {:added "1.2"} 1.385 + [input output & opts] 1.386 + (do-copy input output (when opts (apply hash-map opts)))) 1.387 + 1.388 +(defn ^String as-relative-path 1.389 + "Take an as-file-able thing and return a string if it is 1.390 + a relative path, else IllegalArgumentException." 1.391 + {:added "1.2"} 1.392 + [x] 1.393 + (let [^File f (as-file x)] 1.394 + (if (.isAbsolute f) 1.395 + (throw (IllegalArgumentException. (str f " is not a relative path"))) 1.396 + (.getPath f)))) 1.397 + 1.398 +(defn ^File file 1.399 + "Returns a java.io.File, passing each arg to as-file. Multiple-arg 1.400 + versions treat the first argument as parent and subsequent args as 1.401 + children relative to the parent." 1.402 + {:added "1.2"} 1.403 + ([arg] 1.404 + (as-file arg)) 1.405 + ([parent child] 1.406 + (File. ^File (as-file parent) ^String (as-relative-path child))) 1.407 + ([parent child & more] 1.408 + (reduce file (file parent child) more))) 1.409 + 1.410 +(defn delete-file 1.411 + "Delete file f. Raise an exception if it fails unless silently is true." 1.412 + {:added "1.2"} 1.413 + [f & [silently]] 1.414 + (or (.delete (file f)) 1.415 + silently 1.416 + (throw (java.io.IOException. (str "Couldn't delete " f))))) 1.417 + 1.418 +(defn make-parents 1.419 + "Given the same arg(s) as for file, creates all parent directories of 1.420 + the file they represent." 1.421 + {:added "1.2"} 1.422 + [f & more] 1.423 + (.mkdirs (.getParentFile ^File (apply file f more)))) 1.424 + 1.425 +(defn ^URL resource 1.426 + "Returns the URL for a named resource. Use the context class loader 1.427 + if no loader is specified." 1.428 + {:added "1.2"} 1.429 + ([n] (resource n (.getContextClassLoader (Thread/currentThread)))) 1.430 + ([n ^ClassLoader loader] (.getResource loader n)))