annotate 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
rev   line source
rlm@10 1 ; Copyright (c) Rich Hickey. All rights reserved.
rlm@10 2 ; The use and distribution terms for this software are covered by the
rlm@10 3 ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
rlm@10 4 ; which can be found in the file epl-v10.html at the root of this distribution.
rlm@10 5 ; By using this software in any fashion, you are agreeing to be bound by
rlm@10 6 ; the terms of this license.
rlm@10 7 ; You must not remove this notice, or any other, from this software.
rlm@10 8
rlm@10 9 (ns
rlm@10 10 ^{:author "Stuart Sierra, Chas Emerick, Stuart Halloway",
rlm@10 11 :doc "This file defines polymorphic I/O utility functions for Clojure."}
rlm@10 12 clojure.java.io
rlm@10 13 (:import
rlm@10 14 (java.io Reader InputStream InputStreamReader PushbackReader
rlm@10 15 BufferedReader File OutputStream
rlm@10 16 OutputStreamWriter BufferedWriter Writer
rlm@10 17 FileInputStream FileOutputStream ByteArrayOutputStream
rlm@10 18 StringReader ByteArrayInputStream
rlm@10 19 BufferedInputStream BufferedOutputStream
rlm@10 20 CharArrayReader Closeable)
rlm@10 21 (java.net URI URL MalformedURLException Socket)))
rlm@10 22
rlm@10 23 (def
rlm@10 24 ^{:doc "Type object for a Java primitive byte array."
rlm@10 25 :private true
rlm@10 26 }
rlm@10 27 byte-array-type (class (make-array Byte/TYPE 0)))
rlm@10 28
rlm@10 29 (def
rlm@10 30 ^{:doc "Type object for a Java primitive char array."
rlm@10 31 :private true}
rlm@10 32 char-array-type (class (make-array Character/TYPE 0)))
rlm@10 33
rlm@10 34 (defprotocol ^{:added "1.2"} Coercions
rlm@10 35 "Coerce between various 'resource-namish' things."
rlm@10 36 (^{:tag java.io.File, :added "1.2"} as-file [x] "Coerce argument to a file.")
rlm@10 37 (^{:tag java.net.URL, :added "1.2"} as-url [x] "Coerce argument to a URL."))
rlm@10 38
rlm@10 39 (extend-protocol Coercions
rlm@10 40 nil
rlm@10 41 (as-file [_] nil)
rlm@10 42 (as-url [_] nil)
rlm@10 43
rlm@10 44 String
rlm@10 45 (as-file [s] (File. s))
rlm@10 46 (as-url [s] (URL. s))
rlm@10 47
rlm@10 48 File
rlm@10 49 (as-file [f] f)
rlm@10 50 (as-url [f] (.toURL f))
rlm@10 51
rlm@10 52 URL
rlm@10 53 (as-url [u] u)
rlm@10 54 (as-file [u]
rlm@10 55 (if (= "file" (.getProtocol u))
rlm@10 56 (as-file (.getPath u))
rlm@10 57 (throw (IllegalArgumentException. "Not a file: " u))))
rlm@10 58
rlm@10 59 URI
rlm@10 60 (as-url [u] (.toURL u))
rlm@10 61 (as-file [u] (as-file (as-url u))))
rlm@10 62
rlm@10 63 (defprotocol ^{:added "1.2"} IOFactory
rlm@10 64 "Factory functions that create ready-to-use, buffered versions of
rlm@10 65 the various Java I/O stream types, on top of anything that can
rlm@10 66 be unequivocally converted to the requested kind of stream.
rlm@10 67
rlm@10 68 Common options include
rlm@10 69
rlm@10 70 :append true to open stream in append mode
rlm@10 71 :encoding string name of encoding to use, e.g. \"UTF-8\".
rlm@10 72
rlm@10 73 Callers should generally prefer the higher level API provided by
rlm@10 74 reader, writer, input-stream, and output-stream."
rlm@10 75 (^{:added "1.2"} make-reader [x opts] "Creates a BufferedReader. See also IOFactory docs.")
rlm@10 76 (^{:added "1.2"} make-writer [x opts] "Creates a BufferedWriter. See also IOFactory docs.")
rlm@10 77 (^{:added "1.2"} make-input-stream [x opts] "Creates a BufferedInputStream. See also IOFactory docs.")
rlm@10 78 (^{:added "1.2"} make-output-stream [x opts] "Creates a BufferedOutputStream. See also IOFactory docs."))
rlm@10 79
rlm@10 80 (defn ^Reader reader
rlm@10 81 "Attempts to coerce its argument into an open java.io.Reader.
rlm@10 82 Default implementations always return a java.io.BufferedReader.
rlm@10 83
rlm@10 84 Default implementations are provided for Reader, BufferedReader,
rlm@10 85 InputStream, File, URI, URL, Socket, byte arrays, character arrays,
rlm@10 86 and String.
rlm@10 87
rlm@10 88 If argument is a String, it tries to resolve it first as a URI, then
rlm@10 89 as a local file name. URIs with a 'file' protocol are converted to
rlm@10 90 local file names.
rlm@10 91
rlm@10 92 Should be used inside with-open to ensure the Reader is properly
rlm@10 93 closed."
rlm@10 94 {:added "1.2"}
rlm@10 95 [x & opts]
rlm@10 96 (make-reader x (when opts (apply hash-map opts))))
rlm@10 97
rlm@10 98 (defn ^Writer writer
rlm@10 99 "Attempts to coerce its argument into an open java.io.Writer.
rlm@10 100 Default implementations always return a java.io.BufferedWriter.
rlm@10 101
rlm@10 102 Default implementations are provided for Writer, BufferedWriter,
rlm@10 103 OutputStream, File, URI, URL, Socket, and String.
rlm@10 104
rlm@10 105 If the argument is a String, it tries to resolve it first as a URI, then
rlm@10 106 as a local file name. URIs with a 'file' protocol are converted to
rlm@10 107 local file names.
rlm@10 108
rlm@10 109 Should be used inside with-open to ensure the Writer is properly
rlm@10 110 closed."
rlm@10 111 {:added "1.2"}
rlm@10 112 [x & opts]
rlm@10 113 (make-writer x (when opts (apply hash-map opts))))
rlm@10 114
rlm@10 115 (defn ^InputStream input-stream
rlm@10 116 "Attempts to coerce its argument into an open java.io.InputStream.
rlm@10 117 Default implementations always return a java.io.BufferedInputStream.
rlm@10 118
rlm@10 119 Default implementations are defined for OutputStream, File, URI, URL,
rlm@10 120 Socket, byte array, and String arguments.
rlm@10 121
rlm@10 122 If the argument is a String, it tries to resolve it first as a URI, then
rlm@10 123 as a local file name. URIs with a 'file' protocol are converted to
rlm@10 124 local file names.
rlm@10 125
rlm@10 126 Should be used inside with-open to ensure the InputStream is properly
rlm@10 127 closed."
rlm@10 128 {:added "1.2"}
rlm@10 129 [x & opts]
rlm@10 130 (make-input-stream x (when opts (apply hash-map opts))))
rlm@10 131
rlm@10 132 (defn ^OutputStream output-stream
rlm@10 133 "Attempts to coerce its argument into an open java.io.OutputStream.
rlm@10 134 Default implementations always return a java.io.BufferedOutputStream.
rlm@10 135
rlm@10 136 Default implementations are defined for OutputStream, File, URI, URL,
rlm@10 137 Socket, and String arguments.
rlm@10 138
rlm@10 139 If the argument is a String, it tries to resolve it first as a URI, then
rlm@10 140 as a local file name. URIs with a 'file' protocol are converted to
rlm@10 141 local file names.
rlm@10 142
rlm@10 143 Should be used inside with-open to ensure the OutputStream is
rlm@10 144 properly closed."
rlm@10 145 {:added "1.2"}
rlm@10 146 [x & opts]
rlm@10 147 (make-output-stream x (when opts (apply hash-map opts))))
rlm@10 148
rlm@10 149 (defn- ^Boolean append? [opts]
rlm@10 150 (boolean (:append opts)))
rlm@10 151
rlm@10 152 (defn- ^String encoding [opts]
rlm@10 153 (or (:encoding opts) "UTF-8"))
rlm@10 154
rlm@10 155 (defn- buffer-size [opts]
rlm@10 156 (or (:buffer-size opts) 1024))
rlm@10 157
rlm@10 158 (def default-streams-impl
rlm@10 159 {:make-reader (fn [x opts] (make-reader (make-input-stream x opts) opts))
rlm@10 160 :make-writer (fn [x opts] (make-writer (make-output-stream x opts) opts))
rlm@10 161 :make-input-stream (fn [x opts]
rlm@10 162 (throw (IllegalArgumentException.
rlm@10 163 (str "Cannot open <" (pr-str x) "> as an InputStream."))))
rlm@10 164 :make-output-stream (fn [x opts]
rlm@10 165 (throw (IllegalArgumentException.
rlm@10 166 (str "Cannot open <" (pr-str x) "> as an OutputStream."))))})
rlm@10 167
rlm@10 168 (defn- inputstream->reader
rlm@10 169 [^InputStream is opts]
rlm@10 170 (make-reader (InputStreamReader. is (encoding opts)) opts))
rlm@10 171
rlm@10 172 (defn- outputstream->writer
rlm@10 173 [^OutputStream os opts]
rlm@10 174 (make-writer (OutputStreamWriter. os (encoding opts)) opts))
rlm@10 175
rlm@10 176 (extend BufferedInputStream
rlm@10 177 IOFactory
rlm@10 178 (assoc default-streams-impl
rlm@10 179 :make-input-stream (fn [x opts] x)
rlm@10 180 :make-reader inputstream->reader))
rlm@10 181
rlm@10 182 (extend InputStream
rlm@10 183 IOFactory
rlm@10 184 (assoc default-streams-impl
rlm@10 185 :make-input-stream (fn [x opts] (BufferedInputStream. x))
rlm@10 186 :make-reader inputstream->reader))
rlm@10 187
rlm@10 188 (extend Reader
rlm@10 189 IOFactory
rlm@10 190 (assoc default-streams-impl
rlm@10 191 :make-reader (fn [x opts] (BufferedReader. x))))
rlm@10 192
rlm@10 193 (extend BufferedReader
rlm@10 194 IOFactory
rlm@10 195 (assoc default-streams-impl
rlm@10 196 :make-reader (fn [x opts] x)))
rlm@10 197
rlm@10 198 (extend Writer
rlm@10 199 IOFactory
rlm@10 200 (assoc default-streams-impl
rlm@10 201 :make-writer (fn [x opts] (BufferedWriter. x))))
rlm@10 202
rlm@10 203 (extend BufferedWriter
rlm@10 204 IOFactory
rlm@10 205 (assoc default-streams-impl
rlm@10 206 :make-writer (fn [x opts] x)))
rlm@10 207
rlm@10 208 (extend OutputStream
rlm@10 209 IOFactory
rlm@10 210 (assoc default-streams-impl
rlm@10 211 :make-output-stream (fn [x opts] (BufferedOutputStream. x))
rlm@10 212 :make-writer outputstream->writer))
rlm@10 213
rlm@10 214 (extend BufferedOutputStream
rlm@10 215 IOFactory
rlm@10 216 (assoc default-streams-impl
rlm@10 217 :make-output-stream (fn [x opts] x)
rlm@10 218 :make-writer outputstream->writer))
rlm@10 219
rlm@10 220 (extend File
rlm@10 221 IOFactory
rlm@10 222 (assoc default-streams-impl
rlm@10 223 :make-input-stream (fn [^File x opts] (make-input-stream (FileInputStream. x) opts))
rlm@10 224 :make-output-stream (fn [^File x opts] (make-output-stream (FileOutputStream. x (append? opts)) opts))))
rlm@10 225
rlm@10 226 (extend URL
rlm@10 227 IOFactory
rlm@10 228 (assoc default-streams-impl
rlm@10 229 :make-input-stream (fn [^URL x opts]
rlm@10 230 (make-input-stream
rlm@10 231 (if (= "file" (.getProtocol x))
rlm@10 232 (FileInputStream. (.getPath x))
rlm@10 233 (.openStream x)) opts))
rlm@10 234 :make-output-stream (fn [^URL x opts]
rlm@10 235 (if (= "file" (.getProtocol x))
rlm@10 236 (make-output-stream (File. (.getPath x)) opts)
rlm@10 237 (throw (IllegalArgumentException. (str "Can not write to non-file URL <" x ">")))))))
rlm@10 238
rlm@10 239 (extend URI
rlm@10 240 IOFactory
rlm@10 241 (assoc default-streams-impl
rlm@10 242 :make-input-stream (fn [^URI x opts] (make-input-stream (.toURL x) opts))
rlm@10 243 :make-output-stream (fn [^URI x opts] (make-output-stream (.toURL x) opts))))
rlm@10 244
rlm@10 245 (extend String
rlm@10 246 IOFactory
rlm@10 247 (assoc default-streams-impl
rlm@10 248 :make-input-stream (fn [^String x opts]
rlm@10 249 (try
rlm@10 250 (make-input-stream (URL. x) opts)
rlm@10 251 (catch MalformedURLException e
rlm@10 252 (make-input-stream (File. x) opts))))
rlm@10 253 :make-output-stream (fn [^String x opts]
rlm@10 254 (try
rlm@10 255 (make-output-stream (URL. x) opts)
rlm@10 256 (catch MalformedURLException err
rlm@10 257 (make-output-stream (File. x) opts))))))
rlm@10 258
rlm@10 259 (extend Socket
rlm@10 260 IOFactory
rlm@10 261 (assoc default-streams-impl
rlm@10 262 :make-input-stream (fn [^Socket x opts] (make-input-stream (.getInputStream x) opts))
rlm@10 263 :make-output-stream (fn [^Socket x opts] (make-output-stream (.getOutputStream x) opts))))
rlm@10 264
rlm@10 265 (extend byte-array-type
rlm@10 266 IOFactory
rlm@10 267 (assoc default-streams-impl
rlm@10 268 :make-input-stream (fn [x opts] (make-input-stream (ByteArrayInputStream. x) opts))))
rlm@10 269
rlm@10 270 (extend char-array-type
rlm@10 271 IOFactory
rlm@10 272 (assoc default-streams-impl
rlm@10 273 :make-reader (fn [x opts] (make-reader (CharArrayReader. x) opts))))
rlm@10 274
rlm@10 275 (extend Object
rlm@10 276 IOFactory
rlm@10 277 default-streams-impl)
rlm@10 278
rlm@10 279 (defmulti
rlm@10 280 #^{:doc "Internal helper for copy"
rlm@10 281 :private true
rlm@10 282 :arglists '([input output opts])}
rlm@10 283 do-copy
rlm@10 284 (fn [input output opts] [(type input) (type output)]))
rlm@10 285
rlm@10 286 (defmethod do-copy [InputStream OutputStream] [#^InputStream input #^OutputStream output opts]
rlm@10 287 (let [buffer (make-array Byte/TYPE (buffer-size opts))]
rlm@10 288 (loop []
rlm@10 289 (let [size (.read input buffer)]
rlm@10 290 (when (pos? size)
rlm@10 291 (do (.write output buffer 0 size)
rlm@10 292 (recur)))))))
rlm@10 293
rlm@10 294 (defmethod do-copy [InputStream Writer] [#^InputStream input #^Writer output opts]
rlm@10 295 (let [#^"[B" buffer (make-array Byte/TYPE (buffer-size opts))]
rlm@10 296 (loop []
rlm@10 297 (let [size (.read input buffer)]
rlm@10 298 (when (pos? size)
rlm@10 299 (let [chars (.toCharArray (String. buffer 0 size (encoding opts)))]
rlm@10 300 (do (.write output chars)
rlm@10 301 (recur))))))))
rlm@10 302
rlm@10 303 (defmethod do-copy [InputStream File] [#^InputStream input #^File output opts]
rlm@10 304 (with-open [out (FileOutputStream. output)]
rlm@10 305 (do-copy input out opts)))
rlm@10 306
rlm@10 307 (defmethod do-copy [Reader OutputStream] [#^Reader input #^OutputStream output opts]
rlm@10 308 (let [#^"[C" buffer (make-array Character/TYPE (buffer-size opts))]
rlm@10 309 (loop []
rlm@10 310 (let [size (.read input buffer)]
rlm@10 311 (when (pos? size)
rlm@10 312 (let [bytes (.getBytes (String. buffer 0 size) (encoding opts))]
rlm@10 313 (do (.write output bytes)
rlm@10 314 (recur))))))))
rlm@10 315
rlm@10 316 (defmethod do-copy [Reader Writer] [#^Reader input #^Writer output opts]
rlm@10 317 (let [#^"[C" buffer (make-array Character/TYPE (buffer-size opts))]
rlm@10 318 (loop []
rlm@10 319 (let [size (.read input buffer)]
rlm@10 320 (when (pos? size)
rlm@10 321 (do (.write output buffer 0 size)
rlm@10 322 (recur)))))))
rlm@10 323
rlm@10 324 (defmethod do-copy [Reader File] [#^Reader input #^File output opts]
rlm@10 325 (with-open [out (FileOutputStream. output)]
rlm@10 326 (do-copy input out opts)))
rlm@10 327
rlm@10 328 (defmethod do-copy [File OutputStream] [#^File input #^OutputStream output opts]
rlm@10 329 (with-open [in (FileInputStream. input)]
rlm@10 330 (do-copy in output opts)))
rlm@10 331
rlm@10 332 (defmethod do-copy [File Writer] [#^File input #^Writer output opts]
rlm@10 333 (with-open [in (FileInputStream. input)]
rlm@10 334 (do-copy in output opts)))
rlm@10 335
rlm@10 336 (defmethod do-copy [File File] [#^File input #^File output opts]
rlm@10 337 (with-open [in (FileInputStream. input)
rlm@10 338 out (FileOutputStream. output)]
rlm@10 339 (do-copy in out opts)))
rlm@10 340
rlm@10 341 (defmethod do-copy [String OutputStream] [#^String input #^OutputStream output opts]
rlm@10 342 (do-copy (StringReader. input) output opts))
rlm@10 343
rlm@10 344 (defmethod do-copy [String Writer] [#^String input #^Writer output opts]
rlm@10 345 (do-copy (StringReader. input) output opts))
rlm@10 346
rlm@10 347 (defmethod do-copy [String File] [#^String input #^File output opts]
rlm@10 348 (do-copy (StringReader. input) output opts))
rlm@10 349
rlm@10 350 (defmethod do-copy [char-array-type OutputStream] [input #^OutputStream output opts]
rlm@10 351 (do-copy (CharArrayReader. input) output opts))
rlm@10 352
rlm@10 353 (defmethod do-copy [char-array-type Writer] [input #^Writer output opts]
rlm@10 354 (do-copy (CharArrayReader. input) output opts))
rlm@10 355
rlm@10 356 (defmethod do-copy [char-array-type File] [input #^File output opts]
rlm@10 357 (do-copy (CharArrayReader. input) output opts))
rlm@10 358
rlm@10 359 (defmethod do-copy [byte-array-type OutputStream] [#^"[B" input #^OutputStream output opts]
rlm@10 360 (do-copy (ByteArrayInputStream. input) output opts))
rlm@10 361
rlm@10 362 (defmethod do-copy [byte-array-type Writer] [#^"[B" input #^Writer output opts]
rlm@10 363 (do-copy (ByteArrayInputStream. input) output opts))
rlm@10 364
rlm@10 365 (defmethod do-copy [byte-array-type File] [#^"[B" input #^Writer output opts]
rlm@10 366 (do-copy (ByteArrayInputStream. input) output opts))
rlm@10 367
rlm@10 368 (defn copy
rlm@10 369 "Copies input to output. Returns nil or throws IOException.
rlm@10 370 Input may be an InputStream, Reader, File, byte[], or String.
rlm@10 371 Output may be an OutputStream, Writer, or File.
rlm@10 372
rlm@10 373 Options are key/value pairs and may be one of
rlm@10 374
rlm@10 375 :buffer-size buffer size to use, default is 1024.
rlm@10 376 :encoding encoding to use if converting between
rlm@10 377 byte and char streams.
rlm@10 378
rlm@10 379 Does not close any streams except those it opens itself
rlm@10 380 (on a File)."
rlm@10 381 {:added "1.2"}
rlm@10 382 [input output & opts]
rlm@10 383 (do-copy input output (when opts (apply hash-map opts))))
rlm@10 384
rlm@10 385 (defn ^String as-relative-path
rlm@10 386 "Take an as-file-able thing and return a string if it is
rlm@10 387 a relative path, else IllegalArgumentException."
rlm@10 388 {:added "1.2"}
rlm@10 389 [x]
rlm@10 390 (let [^File f (as-file x)]
rlm@10 391 (if (.isAbsolute f)
rlm@10 392 (throw (IllegalArgumentException. (str f " is not a relative path")))
rlm@10 393 (.getPath f))))
rlm@10 394
rlm@10 395 (defn ^File file
rlm@10 396 "Returns a java.io.File, passing each arg to as-file. Multiple-arg
rlm@10 397 versions treat the first argument as parent and subsequent args as
rlm@10 398 children relative to the parent."
rlm@10 399 {:added "1.2"}
rlm@10 400 ([arg]
rlm@10 401 (as-file arg))
rlm@10 402 ([parent child]
rlm@10 403 (File. ^File (as-file parent) ^String (as-relative-path child)))
rlm@10 404 ([parent child & more]
rlm@10 405 (reduce file (file parent child) more)))
rlm@10 406
rlm@10 407 (defn delete-file
rlm@10 408 "Delete file f. Raise an exception if it fails unless silently is true."
rlm@10 409 {:added "1.2"}
rlm@10 410 [f & [silently]]
rlm@10 411 (or (.delete (file f))
rlm@10 412 silently
rlm@10 413 (throw (java.io.IOException. (str "Couldn't delete " f)))))
rlm@10 414
rlm@10 415 (defn make-parents
rlm@10 416 "Given the same arg(s) as for file, creates all parent directories of
rlm@10 417 the file they represent."
rlm@10 418 {:added "1.2"}
rlm@10 419 [f & more]
rlm@10 420 (.mkdirs (.getParentFile ^File (apply file f more))))
rlm@10 421
rlm@10 422 (defn ^URL resource
rlm@10 423 "Returns the URL for a named resource. Use the context class loader
rlm@10 424 if no loader is specified."
rlm@10 425 {:added "1.2"}
rlm@10 426 ([n] (resource n (.getContextClassLoader (Thread/currentThread))))
rlm@10 427 ([n ^ClassLoader loader] (.getResource loader n)))