comparison 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
comparison
equal deleted inserted replaced
9:35cf337adfcf 10:ef7dbbd6452c
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.
8
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)))
22
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)))
28
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)))
33
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."))
38
39 (extend-protocol Coercions
40 nil
41 (as-file [_] nil)
42 (as-url [_] nil)
43
44 String
45 (as-file [s] (File. s))
46 (as-url [s] (URL. s))
47
48 File
49 (as-file [f] f)
50 (as-url [f] (.toURL f))
51
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))))
58
59 URI
60 (as-url [u] (.toURL u))
61 (as-file [u] (as-file (as-url u))))
62
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.
67
68 Common options include
69
70 :append true to open stream in append mode
71 :encoding string name of encoding to use, e.g. \"UTF-8\".
72
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."))
79
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.
83
84 Default implementations are provided for Reader, BufferedReader,
85 InputStream, File, URI, URL, Socket, byte arrays, character arrays,
86 and String.
87
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.
91
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))))
97
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.
101
102 Default implementations are provided for Writer, BufferedWriter,
103 OutputStream, File, URI, URL, Socket, and String.
104
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.
108
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))))
114
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.
118
119 Default implementations are defined for OutputStream, File, URI, URL,
120 Socket, byte array, and String arguments.
121
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.
125
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))))
131
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.
135
136 Default implementations are defined for OutputStream, File, URI, URL,
137 Socket, and String arguments.
138
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.
142
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))))
148
149 (defn- ^Boolean append? [opts]
150 (boolean (:append opts)))
151
152 (defn- ^String encoding [opts]
153 (or (:encoding opts) "UTF-8"))
154
155 (defn- buffer-size [opts]
156 (or (:buffer-size opts) 1024))
157
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."))))})
167
168 (defn- inputstream->reader
169 [^InputStream is opts]
170 (make-reader (InputStreamReader. is (encoding opts)) opts))
171
172 (defn- outputstream->writer
173 [^OutputStream os opts]
174 (make-writer (OutputStreamWriter. os (encoding opts)) opts))
175
176 (extend BufferedInputStream
177 IOFactory
178 (assoc default-streams-impl
179 :make-input-stream (fn [x opts] x)
180 :make-reader inputstream->reader))
181
182 (extend InputStream
183 IOFactory
184 (assoc default-streams-impl
185 :make-input-stream (fn [x opts] (BufferedInputStream. x))
186 :make-reader inputstream->reader))
187
188 (extend Reader
189 IOFactory
190 (assoc default-streams-impl
191 :make-reader (fn [x opts] (BufferedReader. x))))
192
193 (extend BufferedReader
194 IOFactory
195 (assoc default-streams-impl
196 :make-reader (fn [x opts] x)))
197
198 (extend Writer
199 IOFactory
200 (assoc default-streams-impl
201 :make-writer (fn [x opts] (BufferedWriter. x))))
202
203 (extend BufferedWriter
204 IOFactory
205 (assoc default-streams-impl
206 :make-writer (fn [x opts] x)))
207
208 (extend OutputStream
209 IOFactory
210 (assoc default-streams-impl
211 :make-output-stream (fn [x opts] (BufferedOutputStream. x))
212 :make-writer outputstream->writer))
213
214 (extend BufferedOutputStream
215 IOFactory
216 (assoc default-streams-impl
217 :make-output-stream (fn [x opts] x)
218 :make-writer outputstream->writer))
219
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))))
225
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 ">")))))))
238
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))))
244
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))))))
258
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))))
264
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))))
269
270 (extend char-array-type
271 IOFactory
272 (assoc default-streams-impl
273 :make-reader (fn [x opts] (make-reader (CharArrayReader. x) opts))))
274
275 (extend Object
276 IOFactory
277 default-streams-impl)
278
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)]))
285
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)))))))
293
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))))))))
302
303 (defmethod do-copy [InputStream File] [#^InputStream input #^File output opts]
304 (with-open [out (FileOutputStream. output)]
305 (do-copy input out opts)))
306
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))))))))
315
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)))))))
323
324 (defmethod do-copy [Reader File] [#^Reader input #^File output opts]
325 (with-open [out (FileOutputStream. output)]
326 (do-copy input out opts)))
327
328 (defmethod do-copy [File OutputStream] [#^File input #^OutputStream output opts]
329 (with-open [in (FileInputStream. input)]
330 (do-copy in output opts)))
331
332 (defmethod do-copy [File Writer] [#^File input #^Writer output opts]
333 (with-open [in (FileInputStream. input)]
334 (do-copy in output opts)))
335
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)))
340
341 (defmethod do-copy [String OutputStream] [#^String input #^OutputStream output opts]
342 (do-copy (StringReader. input) output opts))
343
344 (defmethod do-copy [String Writer] [#^String input #^Writer output opts]
345 (do-copy (StringReader. input) output opts))
346
347 (defmethod do-copy [String File] [#^String input #^File output opts]
348 (do-copy (StringReader. input) output opts))
349
350 (defmethod do-copy [char-array-type OutputStream] [input #^OutputStream output opts]
351 (do-copy (CharArrayReader. input) output opts))
352
353 (defmethod do-copy [char-array-type Writer] [input #^Writer output opts]
354 (do-copy (CharArrayReader. input) output opts))
355
356 (defmethod do-copy [char-array-type File] [input #^File output opts]
357 (do-copy (CharArrayReader. input) output opts))
358
359 (defmethod do-copy [byte-array-type OutputStream] [#^"[B" input #^OutputStream output opts]
360 (do-copy (ByteArrayInputStream. input) output opts))
361
362 (defmethod do-copy [byte-array-type Writer] [#^"[B" input #^Writer output opts]
363 (do-copy (ByteArrayInputStream. input) output opts))
364
365 (defmethod do-copy [byte-array-type File] [#^"[B" input #^Writer output opts]
366 (do-copy (ByteArrayInputStream. input) output opts))
367
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.
372
373 Options are key/value pairs and may be one of
374
375 :buffer-size buffer size to use, default is 1024.
376 :encoding encoding to use if converting between
377 byte and char streams.
378
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))))
384
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))))
394
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)))
406
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)))))
414
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))))
421
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)))