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