Mercurial > lasercutter
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))) |