rlm@10
|
1 ;;; io.clj -- duck-typed I/O streams for Clojure
|
rlm@10
|
2
|
rlm@10
|
3 ;; by Stuart Sierra, http://stuartsierra.com/
|
rlm@10
|
4 ;; May 13, 2009
|
rlm@10
|
5
|
rlm@10
|
6 ;; Copyright (c) Stuart Sierra, 2009. All rights reserved. The use
|
rlm@10
|
7 ;; and distribution terms for this software are covered by the Eclipse
|
rlm@10
|
8 ;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
|
rlm@10
|
9 ;; which can be found in the file epl-v10.html at the root of this
|
rlm@10
|
10 ;; distribution. By using this software in any fashion, you are
|
rlm@10
|
11 ;; agreeing to be bound by the terms of this license. You must not
|
rlm@10
|
12 ;; remove this notice, or any other, from this software.
|
rlm@10
|
13
|
rlm@10
|
14
|
rlm@10
|
15 ;; This file defines "duck-typed" I/O utility functions for Clojure.
|
rlm@10
|
16 ;; The 'reader' and 'writer' functions will open and return an
|
rlm@10
|
17 ;; instance of java.io.BufferedReader and java.io.BufferedWriter,
|
rlm@10
|
18 ;; respectively, for a variety of argument types -- filenames as
|
rlm@10
|
19 ;; strings, URLs, java.io.File's, etc. 'reader' even works on http
|
rlm@10
|
20 ;; URLs.
|
rlm@10
|
21 ;;
|
rlm@10
|
22 ;; Note: this is not really "duck typing" as implemented in languages
|
rlm@10
|
23 ;; like Ruby. A better name would have been "do-what-I-mean-streams"
|
rlm@10
|
24 ;; or "just-give-me-a-stream", but ducks are funnier.
|
rlm@10
|
25
|
rlm@10
|
26
|
rlm@10
|
27 ;; CHANGE LOG
|
rlm@10
|
28 ;;
|
rlm@10
|
29 ;; July 23, 2010: Most functions here are deprecated. Use
|
rlm@10
|
30 ;; clojure.java.io
|
rlm@10
|
31 ;;
|
rlm@10
|
32 ;; May 13, 2009: added functions to open writers for appending
|
rlm@10
|
33 ;;
|
rlm@10
|
34 ;; May 3, 2009: renamed file to file-str, for compatibility with
|
rlm@10
|
35 ;; clojure.contrib.java. reader/writer no longer use this
|
rlm@10
|
36 ;; function.
|
rlm@10
|
37 ;;
|
rlm@10
|
38 ;; February 16, 2009: (lazy branch) fixed read-lines to work with lazy
|
rlm@10
|
39 ;; Clojure.
|
rlm@10
|
40 ;;
|
rlm@10
|
41 ;; January 10, 2009: added *default-encoding*, so streams are always
|
rlm@10
|
42 ;; opened as UTF-8.
|
rlm@10
|
43 ;;
|
rlm@10
|
44 ;; December 19, 2008: rewrote reader and writer as multimethods; added
|
rlm@10
|
45 ;; slurp*, file, and read-lines
|
rlm@10
|
46 ;;
|
rlm@10
|
47 ;; April 8, 2008: first version
|
rlm@10
|
48
|
rlm@10
|
49
|
rlm@10
|
50
|
rlm@10
|
51 (ns
|
rlm@10
|
52 ^{:author "Stuart Sierra",
|
rlm@10
|
53 :doc "This file defines polymorphic I/O utility functions for Clojure.
|
rlm@10
|
54
|
rlm@10
|
55 The Streams protocol defines reader, writer, input-stream and
|
rlm@10
|
56 output-stream methods that return BufferedReader, BufferedWriter,
|
rlm@10
|
57 BufferedInputStream and BufferedOutputStream instances (respectively),
|
rlm@10
|
58 with default implementations extended to a variety of argument
|
rlm@10
|
59 types: URLs or filenames as strings, java.io.File's, Sockets, etc."}
|
rlm@10
|
60 clojure.contrib.io
|
rlm@10
|
61 (:refer-clojure :exclude (spit))
|
rlm@10
|
62 (:import
|
rlm@10
|
63 (java.io Reader InputStream InputStreamReader PushbackReader
|
rlm@10
|
64 BufferedReader File OutputStream
|
rlm@10
|
65 OutputStreamWriter BufferedWriter Writer
|
rlm@10
|
66 FileInputStream FileOutputStream ByteArrayOutputStream
|
rlm@10
|
67 StringReader ByteArrayInputStream
|
rlm@10
|
68 BufferedInputStream BufferedOutputStream
|
rlm@10
|
69 CharArrayReader)
|
rlm@10
|
70 (java.net URI URL MalformedURLException Socket)))
|
rlm@10
|
71
|
rlm@10
|
72
|
rlm@10
|
73 (def
|
rlm@10
|
74 ^{:doc "Name of the default encoding to use when reading & writing.
|
rlm@10
|
75 Default is UTF-8."
|
rlm@10
|
76 :tag "java.lang.String"}
|
rlm@10
|
77 *default-encoding* "UTF-8")
|
rlm@10
|
78
|
rlm@10
|
79 (def
|
rlm@10
|
80 ^{:doc "Size, in bytes or characters, of the buffer used when
|
rlm@10
|
81 copying streams."}
|
rlm@10
|
82 *buffer-size* 1024)
|
rlm@10
|
83
|
rlm@10
|
84 (def
|
rlm@10
|
85 ^{:doc "Type object for a Java primitive byte array."}
|
rlm@10
|
86 *byte-array-type* (class (make-array Byte/TYPE 0)))
|
rlm@10
|
87
|
rlm@10
|
88 (def
|
rlm@10
|
89 ^{:doc "Type object for a Java primitive char array."}
|
rlm@10
|
90 *char-array-type* (class (make-array Character/TYPE 0)))
|
rlm@10
|
91
|
rlm@10
|
92
|
rlm@10
|
93 (defn ^File file-str
|
rlm@10
|
94 "Concatenates args as strings and returns a java.io.File. Replaces
|
rlm@10
|
95 all / and \\ with File/separatorChar. Replaces ~ at the start of
|
rlm@10
|
96 the path with the user.home system property."
|
rlm@10
|
97 [& args]
|
rlm@10
|
98 (let [^String s (apply str args)
|
rlm@10
|
99 s (.replace s \\ File/separatorChar)
|
rlm@10
|
100 s (.replace s \/ File/separatorChar)
|
rlm@10
|
101 s (if (.startsWith s "~")
|
rlm@10
|
102 (str (System/getProperty "user.home")
|
rlm@10
|
103 File/separator (subs s 1))
|
rlm@10
|
104 s)]
|
rlm@10
|
105 (File. s)))
|
rlm@10
|
106
|
rlm@10
|
107 (def
|
rlm@10
|
108 ^{:doc "If true, writer, output-stream and spit will open files in append mode.
|
rlm@10
|
109 Defaults to false. Instead of binding this var directly, use append-writer,
|
rlm@10
|
110 append-output-stream or append-spit."
|
rlm@10
|
111 :tag "java.lang.Boolean"}
|
rlm@10
|
112 *append* false)
|
rlm@10
|
113
|
rlm@10
|
114 (defn- assert-not-appending []
|
rlm@10
|
115 (when *append*
|
rlm@10
|
116 (throw (Exception. "Cannot change an open stream to append mode."))))
|
rlm@10
|
117
|
rlm@10
|
118 ;; @todo -- Both simple and elaborate methods for controlling buffering of
|
rlm@10
|
119 ;; in the Streams protocol were implemented, considered, and postponed
|
rlm@10
|
120 ;; see http://groups.google.com/group/clojure-dev/browse_frm/thread/3e39e9b3982f542b
|
rlm@10
|
121 (defprotocol Streams
|
rlm@10
|
122 (reader [x]
|
rlm@10
|
123 "Attempts to coerce its argument into an open java.io.Reader.
|
rlm@10
|
124 The default implementations of this protocol always return a
|
rlm@10
|
125 java.io.BufferedReader.
|
rlm@10
|
126
|
rlm@10
|
127 Default implementations are provided for Reader, BufferedReader,
|
rlm@10
|
128 InputStream, File, URI, URL, Socket, byte arrays, character arrays,
|
rlm@10
|
129 and String.
|
rlm@10
|
130
|
rlm@10
|
131 If argument is a String, it tries to resolve it first as a URI, then
|
rlm@10
|
132 as a local file name. URIs with a 'file' protocol are converted to
|
rlm@10
|
133 local file names. If this fails, a final attempt is made to resolve
|
rlm@10
|
134 the string as a resource on the CLASSPATH.
|
rlm@10
|
135
|
rlm@10
|
136 Uses *default-encoding* as the text encoding.
|
rlm@10
|
137
|
rlm@10
|
138 Should be used inside with-open to ensure the Reader is properly
|
rlm@10
|
139 closed.")
|
rlm@10
|
140 (writer [x]
|
rlm@10
|
141 "Attempts to coerce its argument into an open java.io.Writer.
|
rlm@10
|
142 The default implementations of this protocol always return a
|
rlm@10
|
143 java.io.BufferedWriter.
|
rlm@10
|
144
|
rlm@10
|
145 Default implementations are provided for Writer, BufferedWriter,
|
rlm@10
|
146 OutputStream, File, URI, URL, Socket, and String.
|
rlm@10
|
147
|
rlm@10
|
148 If the argument is a String, it tries to resolve it first as a URI, then
|
rlm@10
|
149 as a local file name. URIs with a 'file' protocol are converted to
|
rlm@10
|
150 local file names.
|
rlm@10
|
151
|
rlm@10
|
152 Should be used inside with-open to ensure the Writer is properly
|
rlm@10
|
153 closed.")
|
rlm@10
|
154 (input-stream [x]
|
rlm@10
|
155 "Attempts to coerce its argument into an open java.io.InputStream.
|
rlm@10
|
156 The default implementations of this protocol always return a
|
rlm@10
|
157 java.io.BufferedInputStream.
|
rlm@10
|
158
|
rlm@10
|
159 Default implementations are defined for OutputStream, File, URI, URL,
|
rlm@10
|
160 Socket, byte array, and String arguments.
|
rlm@10
|
161
|
rlm@10
|
162 If the argument is a String, it tries to resolve it first as a URI, then
|
rlm@10
|
163 as a local file name. URIs with a 'file' protocol are converted to
|
rlm@10
|
164 local file names.
|
rlm@10
|
165
|
rlm@10
|
166 Should be used inside with-open to ensure the InputStream is properly
|
rlm@10
|
167 closed.")
|
rlm@10
|
168 (output-stream [x]
|
rlm@10
|
169 "Attempts to coerce its argument into an open java.io.OutputStream.
|
rlm@10
|
170 The default implementations of this protocol always return a
|
rlm@10
|
171 java.io.BufferedOutputStream.
|
rlm@10
|
172
|
rlm@10
|
173 Default implementations are defined for OutputStream, File, URI, URL,
|
rlm@10
|
174 Socket, and String arguments.
|
rlm@10
|
175
|
rlm@10
|
176 If the argument is a String, it tries to resolve it first as a URI, then
|
rlm@10
|
177 as a local file name. URIs with a 'file' protocol are converted to
|
rlm@10
|
178 local file names.
|
rlm@10
|
179
|
rlm@10
|
180 Should be used inside with-open to ensure the OutputStream is
|
rlm@10
|
181 properly closed."))
|
rlm@10
|
182
|
rlm@10
|
183 (def default-streams-impl
|
rlm@10
|
184 {:reader #(reader (input-stream %))
|
rlm@10
|
185 :writer #(writer (output-stream %))
|
rlm@10
|
186 :input-stream #(throw (Exception. (str "Cannot open <" (pr-str %) "> as an InputStream.")))
|
rlm@10
|
187 :output-stream #(throw (Exception. (str "Cannot open <" (pr-str %) "> as an OutputStream.")))})
|
rlm@10
|
188
|
rlm@10
|
189 (extend File
|
rlm@10
|
190 Streams
|
rlm@10
|
191 (assoc default-streams-impl
|
rlm@10
|
192 :input-stream #(input-stream (FileInputStream. ^File %))
|
rlm@10
|
193 :output-stream #(let [stream (FileOutputStream. ^File % *append*)]
|
rlm@10
|
194 (binding [*append* false]
|
rlm@10
|
195 (output-stream stream)))))
|
rlm@10
|
196 (extend URL
|
rlm@10
|
197 Streams
|
rlm@10
|
198 (assoc default-streams-impl
|
rlm@10
|
199 :input-stream (fn [^URL x]
|
rlm@10
|
200 (input-stream (if (= "file" (.getProtocol x))
|
rlm@10
|
201 (FileInputStream. (.getPath x))
|
rlm@10
|
202 (.openStream x))))
|
rlm@10
|
203 :output-stream (fn [^URL x]
|
rlm@10
|
204 (if (= "file" (.getProtocol x))
|
rlm@10
|
205 (output-stream (File. (.getPath x)))
|
rlm@10
|
206 (throw (Exception. (str "Can not write to non-file URL <" x ">")))))))
|
rlm@10
|
207 (extend URI
|
rlm@10
|
208 Streams
|
rlm@10
|
209 (assoc default-streams-impl
|
rlm@10
|
210 :input-stream #(input-stream (.toURL ^URI %))
|
rlm@10
|
211 :output-stream #(output-stream (.toURL ^URI %))))
|
rlm@10
|
212 (extend String
|
rlm@10
|
213 Streams
|
rlm@10
|
214 (assoc default-streams-impl
|
rlm@10
|
215 :input-stream #(try
|
rlm@10
|
216 (input-stream (URL. %))
|
rlm@10
|
217 (catch MalformedURLException e
|
rlm@10
|
218 (input-stream (File. ^String %))))
|
rlm@10
|
219 :output-stream #(try
|
rlm@10
|
220 (output-stream (URL. %))
|
rlm@10
|
221 (catch MalformedURLException err
|
rlm@10
|
222 (output-stream (File. ^String %))))))
|
rlm@10
|
223 (extend Socket
|
rlm@10
|
224 Streams
|
rlm@10
|
225 (assoc default-streams-impl
|
rlm@10
|
226 :input-stream #(.getInputStream ^Socket %)
|
rlm@10
|
227 :output-stream #(output-stream (.getOutputStream ^Socket %))))
|
rlm@10
|
228 (extend *byte-array-type*
|
rlm@10
|
229 Streams
|
rlm@10
|
230 (assoc default-streams-impl :input-stream #(input-stream (ByteArrayInputStream. %))))
|
rlm@10
|
231 (extend *char-array-type*
|
rlm@10
|
232 Streams
|
rlm@10
|
233 (assoc default-streams-impl :reader #(reader (CharArrayReader. %))))
|
rlm@10
|
234 (extend Object
|
rlm@10
|
235 Streams
|
rlm@10
|
236 default-streams-impl)
|
rlm@10
|
237
|
rlm@10
|
238 (extend Reader
|
rlm@10
|
239 Streams
|
rlm@10
|
240 (assoc default-streams-impl :reader #(BufferedReader. %)))
|
rlm@10
|
241 (extend BufferedReader
|
rlm@10
|
242 Streams
|
rlm@10
|
243 (assoc default-streams-impl :reader identity))
|
rlm@10
|
244 (defn- inputstream->reader
|
rlm@10
|
245 [^InputStream is]
|
rlm@10
|
246 (reader (InputStreamReader. is *default-encoding*)))
|
rlm@10
|
247 (extend InputStream
|
rlm@10
|
248 Streams
|
rlm@10
|
249 (assoc default-streams-impl :input-stream #(BufferedInputStream. %)
|
rlm@10
|
250 :reader inputstream->reader))
|
rlm@10
|
251 (extend BufferedInputStream
|
rlm@10
|
252 Streams
|
rlm@10
|
253 (assoc default-streams-impl
|
rlm@10
|
254 :input-stream identity
|
rlm@10
|
255 :reader inputstream->reader))
|
rlm@10
|
256
|
rlm@10
|
257 (extend Writer
|
rlm@10
|
258 Streams
|
rlm@10
|
259 (assoc default-streams-impl :writer #(do (assert-not-appending)
|
rlm@10
|
260 (BufferedWriter. %))))
|
rlm@10
|
261 (extend BufferedWriter
|
rlm@10
|
262 Streams
|
rlm@10
|
263 (assoc default-streams-impl :writer #(do (assert-not-appending) %)))
|
rlm@10
|
264 (defn- outputstream->writer
|
rlm@10
|
265 [^OutputStream os]
|
rlm@10
|
266 (assert-not-appending)
|
rlm@10
|
267 (writer (OutputStreamWriter. os *default-encoding*)))
|
rlm@10
|
268 (extend OutputStream
|
rlm@10
|
269 Streams
|
rlm@10
|
270 (assoc default-streams-impl
|
rlm@10
|
271 :output-stream #(do (assert-not-appending)
|
rlm@10
|
272 (BufferedOutputStream. %))
|
rlm@10
|
273 :writer outputstream->writer))
|
rlm@10
|
274 (extend BufferedOutputStream
|
rlm@10
|
275 Streams
|
rlm@10
|
276 (assoc default-streams-impl
|
rlm@10
|
277 :output-stream #(do (assert-not-appending) %)
|
rlm@10
|
278 :writer outputstream->writer))
|
rlm@10
|
279
|
rlm@10
|
280 (defn append-output-stream
|
rlm@10
|
281 "Like output-stream but opens file for appending. Does not work on streams
|
rlm@10
|
282 that are already open."
|
rlm@10
|
283 {:deprecated "1.2"}
|
rlm@10
|
284 [x]
|
rlm@10
|
285 (binding [*append* true]
|
rlm@10
|
286 (output-stream x)))
|
rlm@10
|
287
|
rlm@10
|
288 (defn append-writer
|
rlm@10
|
289 "Like writer but opens file for appending. Does not work on streams
|
rlm@10
|
290 that are already open."
|
rlm@10
|
291 {:deprecated "1.2"}
|
rlm@10
|
292 [x]
|
rlm@10
|
293 (binding [*append* true]
|
rlm@10
|
294 (writer x)))
|
rlm@10
|
295
|
rlm@10
|
296 (defn write-lines
|
rlm@10
|
297 "Writes lines (a seq) to f, separated by newlines. f is opened with
|
rlm@10
|
298 writer, and automatically closed at the end of the sequence."
|
rlm@10
|
299 [f lines]
|
rlm@10
|
300 (with-open [^BufferedWriter writer (writer f)]
|
rlm@10
|
301 (loop [lines lines]
|
rlm@10
|
302 (when-let [line (first lines)]
|
rlm@10
|
303 (.write writer (str line))
|
rlm@10
|
304 (.newLine writer)
|
rlm@10
|
305 (recur (rest lines))))))
|
rlm@10
|
306
|
rlm@10
|
307 (defn read-lines
|
rlm@10
|
308 "Like clojure.core/line-seq but opens f with reader. Automatically
|
rlm@10
|
309 closes the reader AFTER YOU CONSUME THE ENTIRE SEQUENCE."
|
rlm@10
|
310 [f]
|
rlm@10
|
311 (let [read-line (fn this [^BufferedReader rdr]
|
rlm@10
|
312 (lazy-seq
|
rlm@10
|
313 (if-let [line (.readLine rdr)]
|
rlm@10
|
314 (cons line (this rdr))
|
rlm@10
|
315 (.close rdr))))]
|
rlm@10
|
316 (read-line (reader f))))
|
rlm@10
|
317
|
rlm@10
|
318 (defn ^String slurp*
|
rlm@10
|
319 "Like clojure.core/slurp but opens f with reader."
|
rlm@10
|
320 {:deprecated "1.2"}
|
rlm@10
|
321 [f]
|
rlm@10
|
322 (with-open [^BufferedReader r (reader f)]
|
rlm@10
|
323 (let [sb (StringBuilder.)]
|
rlm@10
|
324 (loop [c (.read r)]
|
rlm@10
|
325 (if (neg? c)
|
rlm@10
|
326 (str sb)
|
rlm@10
|
327 (do (.append sb (char c))
|
rlm@10
|
328 (recur (.read r))))))))
|
rlm@10
|
329
|
rlm@10
|
330 (defn spit
|
rlm@10
|
331 "Opposite of slurp. Opens f with writer, writes content, then
|
rlm@10
|
332 closes f."
|
rlm@10
|
333 {:deprecated "1.2"}
|
rlm@10
|
334 [f content]
|
rlm@10
|
335 (with-open [^Writer w (writer f)]
|
rlm@10
|
336 (.write w content)))
|
rlm@10
|
337
|
rlm@10
|
338 (defn append-spit
|
rlm@10
|
339 "Like spit but appends to file."
|
rlm@10
|
340 {:deprecated "1.2"}
|
rlm@10
|
341 [f content]
|
rlm@10
|
342 (with-open [^Writer w (append-writer f)]
|
rlm@10
|
343 (.write w content)))
|
rlm@10
|
344
|
rlm@10
|
345 (defn pwd
|
rlm@10
|
346 "Returns current working directory as a String. (Like UNIX 'pwd'.)
|
rlm@10
|
347 Note: In Java, you cannot change the current working directory."
|
rlm@10
|
348 {:deprecated "1.2"}
|
rlm@10
|
349 []
|
rlm@10
|
350 (System/getProperty "user.dir"))
|
rlm@10
|
351
|
rlm@10
|
352 (defmacro with-out-writer
|
rlm@10
|
353 "Opens a writer on f, binds it to *out*, and evalutes body.
|
rlm@10
|
354 Anything printed within body will be written to f."
|
rlm@10
|
355 [f & body]
|
rlm@10
|
356 `(with-open [stream# (writer ~f)]
|
rlm@10
|
357 (binding [*out* stream#]
|
rlm@10
|
358 ~@body)))
|
rlm@10
|
359
|
rlm@10
|
360 (defmacro with-out-append-writer
|
rlm@10
|
361 "Like with-out-writer but appends to file."
|
rlm@10
|
362 {:deprecated "1.2"}
|
rlm@10
|
363 [f & body]
|
rlm@10
|
364 `(with-open [stream# (append-writer ~f)]
|
rlm@10
|
365 (binding [*out* stream#]
|
rlm@10
|
366 ~@body)))
|
rlm@10
|
367
|
rlm@10
|
368 (defmacro with-in-reader
|
rlm@10
|
369 "Opens a PushbackReader on f, binds it to *in*, and evaluates body."
|
rlm@10
|
370 [f & body]
|
rlm@10
|
371 `(with-open [stream# (PushbackReader. (reader ~f))]
|
rlm@10
|
372 (binding [*in* stream#]
|
rlm@10
|
373 ~@body)))
|
rlm@10
|
374
|
rlm@10
|
375 (defmulti
|
rlm@10
|
376 ^{:deprecated "1.2"
|
rlm@10
|
377 :doc "Copies input to output. Returns nil.
|
rlm@10
|
378 Input may be an InputStream, Reader, File, byte[], or String.
|
rlm@10
|
379 Output may be an OutputStream, Writer, or File.
|
rlm@10
|
380
|
rlm@10
|
381 Does not close any streams except those it opens itself
|
rlm@10
|
382 (on a File).
|
rlm@10
|
383
|
rlm@10
|
384 Writing a File fails if the parent directory does not exist."
|
rlm@10
|
385 :arglists '([input output])}
|
rlm@10
|
386 copy
|
rlm@10
|
387 (fn [input output] [(type input) (type output)]))
|
rlm@10
|
388
|
rlm@10
|
389 (defmethod copy [InputStream OutputStream] [^InputStream input ^OutputStream output]
|
rlm@10
|
390 (let [buffer (make-array Byte/TYPE *buffer-size*)]
|
rlm@10
|
391 (loop []
|
rlm@10
|
392 (let [size (.read input buffer)]
|
rlm@10
|
393 (when (pos? size)
|
rlm@10
|
394 (do (.write output buffer 0 size)
|
rlm@10
|
395 (recur)))))))
|
rlm@10
|
396
|
rlm@10
|
397 (defmethod copy [InputStream Writer] [^InputStream input ^Writer output]
|
rlm@10
|
398 (let [^"[B" buffer (make-array Byte/TYPE *buffer-size*)]
|
rlm@10
|
399 (loop []
|
rlm@10
|
400 (let [size (.read input buffer)]
|
rlm@10
|
401 (when (pos? size)
|
rlm@10
|
402 (let [chars (.toCharArray (String. buffer 0 size *default-encoding*))]
|
rlm@10
|
403 (do (.write output chars)
|
rlm@10
|
404 (recur))))))))
|
rlm@10
|
405
|
rlm@10
|
406 (defmethod copy [InputStream File] [^InputStream input ^File output]
|
rlm@10
|
407 (with-open [out (FileOutputStream. output)]
|
rlm@10
|
408 (copy input out)))
|
rlm@10
|
409
|
rlm@10
|
410 (defmethod copy [Reader OutputStream] [^Reader input ^OutputStream output]
|
rlm@10
|
411 (let [^"[C" buffer (make-array Character/TYPE *buffer-size*)]
|
rlm@10
|
412 (loop []
|
rlm@10
|
413 (let [size (.read input buffer)]
|
rlm@10
|
414 (when (pos? size)
|
rlm@10
|
415 (let [bytes (.getBytes (String. buffer 0 size) *default-encoding*)]
|
rlm@10
|
416 (do (.write output bytes)
|
rlm@10
|
417 (recur))))))))
|
rlm@10
|
418
|
rlm@10
|
419 (defmethod copy [Reader Writer] [^Reader input ^Writer output]
|
rlm@10
|
420 (let [^"[C" buffer (make-array Character/TYPE *buffer-size*)]
|
rlm@10
|
421 (loop []
|
rlm@10
|
422 (let [size (.read input buffer)]
|
rlm@10
|
423 (when (pos? size)
|
rlm@10
|
424 (do (.write output buffer 0 size)
|
rlm@10
|
425 (recur)))))))
|
rlm@10
|
426
|
rlm@10
|
427 (defmethod copy [Reader File] [^Reader input ^File output]
|
rlm@10
|
428 (with-open [out (FileOutputStream. output)]
|
rlm@10
|
429 (copy input out)))
|
rlm@10
|
430
|
rlm@10
|
431 (defmethod copy [File OutputStream] [^File input ^OutputStream output]
|
rlm@10
|
432 (with-open [in (FileInputStream. input)]
|
rlm@10
|
433 (copy in output)))
|
rlm@10
|
434
|
rlm@10
|
435 (defmethod copy [File Writer] [^File input ^Writer output]
|
rlm@10
|
436 (with-open [in (FileInputStream. input)]
|
rlm@10
|
437 (copy in output)))
|
rlm@10
|
438
|
rlm@10
|
439 (defmethod copy [File File] [^File input ^File output]
|
rlm@10
|
440 (with-open [in (FileInputStream. input)
|
rlm@10
|
441 out (FileOutputStream. output)]
|
rlm@10
|
442 (copy in out)))
|
rlm@10
|
443
|
rlm@10
|
444 (defmethod copy [String OutputStream] [^String input ^OutputStream output]
|
rlm@10
|
445 (copy (StringReader. input) output))
|
rlm@10
|
446
|
rlm@10
|
447 (defmethod copy [String Writer] [^String input ^Writer output]
|
rlm@10
|
448 (copy (StringReader. input) output))
|
rlm@10
|
449
|
rlm@10
|
450 (defmethod copy [String File] [^String input ^File output]
|
rlm@10
|
451 (copy (StringReader. input) output))
|
rlm@10
|
452
|
rlm@10
|
453 (defmethod copy [*char-array-type* OutputStream] [input ^OutputStream output]
|
rlm@10
|
454 (copy (CharArrayReader. input) output))
|
rlm@10
|
455
|
rlm@10
|
456 (defmethod copy [*char-array-type* Writer] [input ^Writer output]
|
rlm@10
|
457 (copy (CharArrayReader. input) output))
|
rlm@10
|
458
|
rlm@10
|
459 (defmethod copy [*char-array-type* File] [input ^File output]
|
rlm@10
|
460 (copy (CharArrayReader. input) output))
|
rlm@10
|
461
|
rlm@10
|
462 (defmethod copy [*byte-array-type* OutputStream] [^"[B" input ^OutputStream output]
|
rlm@10
|
463 (copy (ByteArrayInputStream. input) output))
|
rlm@10
|
464
|
rlm@10
|
465 (defmethod copy [*byte-array-type* Writer] [^"[B" input ^Writer output]
|
rlm@10
|
466 (copy (ByteArrayInputStream. input) output))
|
rlm@10
|
467
|
rlm@10
|
468 (defmethod copy [*byte-array-type* File] [^"[B" input ^Writer output]
|
rlm@10
|
469 (copy (ByteArrayInputStream. input) output))
|
rlm@10
|
470
|
rlm@10
|
471 (defn make-parents
|
rlm@10
|
472 "Creates all parent directories of file."
|
rlm@10
|
473 [^File file]
|
rlm@10
|
474 (.mkdirs (.getParentFile file)))
|
rlm@10
|
475
|
rlm@10
|
476 (defmulti
|
rlm@10
|
477 ^{:doc "Converts argument into a Java byte array. Argument may be
|
rlm@10
|
478 a String, File, InputStream, or Reader. If the argument is already
|
rlm@10
|
479 a byte array, returns it."
|
rlm@10
|
480 :arglists '([arg])}
|
rlm@10
|
481 to-byte-array type)
|
rlm@10
|
482
|
rlm@10
|
483 (defmethod to-byte-array *byte-array-type* [x] x)
|
rlm@10
|
484
|
rlm@10
|
485 (defmethod to-byte-array String [^String x]
|
rlm@10
|
486 (.getBytes x *default-encoding*))
|
rlm@10
|
487
|
rlm@10
|
488 (defmethod to-byte-array File [^File x]
|
rlm@10
|
489 (with-open [input (FileInputStream. x)
|
rlm@10
|
490 buffer (ByteArrayOutputStream.)]
|
rlm@10
|
491 (copy input buffer)
|
rlm@10
|
492 (.toByteArray buffer)))
|
rlm@10
|
493
|
rlm@10
|
494 (defmethod to-byte-array InputStream [^InputStream x]
|
rlm@10
|
495 (let [buffer (ByteArrayOutputStream.)]
|
rlm@10
|
496 (copy x buffer)
|
rlm@10
|
497 (.toByteArray buffer)))
|
rlm@10
|
498
|
rlm@10
|
499 (defmethod to-byte-array Reader [^Reader x]
|
rlm@10
|
500 (.getBytes (slurp* x) *default-encoding*))
|
rlm@10
|
501
|
rlm@10
|
502 (defmulti relative-path-string
|
rlm@10
|
503 "Interpret a String or java.io.File as a relative path string.
|
rlm@10
|
504 Building block for clojure.contrib.java/file."
|
rlm@10
|
505 {:deprecated "1.2"}
|
rlm@10
|
506 class)
|
rlm@10
|
507
|
rlm@10
|
508 (defmethod relative-path-string String [^String s]
|
rlm@10
|
509 (relative-path-string (File. s)))
|
rlm@10
|
510
|
rlm@10
|
511 (defmethod relative-path-string File [^File f]
|
rlm@10
|
512 (if (.isAbsolute f)
|
rlm@10
|
513 (throw (IllegalArgumentException. (str f " is not a relative path")))
|
rlm@10
|
514 (.getPath f)))
|
rlm@10
|
515
|
rlm@10
|
516 (defmulti ^File as-file
|
rlm@10
|
517 "Interpret a String or a java.io.File as a File. Building block
|
rlm@10
|
518 for clojure.contrib.java/file, which you should prefer
|
rlm@10
|
519 in most cases."
|
rlm@10
|
520 {:deprecated "1.2"}
|
rlm@10
|
521 class)
|
rlm@10
|
522 (defmethod as-file String [^String s] (File. s))
|
rlm@10
|
523 (defmethod as-file File [f] f)
|
rlm@10
|
524
|
rlm@10
|
525 (defn ^File file
|
rlm@10
|
526 "Returns a java.io.File from string or file args."
|
rlm@10
|
527 {:deprecated "1.2"}
|
rlm@10
|
528 ([arg]
|
rlm@10
|
529 (as-file arg))
|
rlm@10
|
530 ([parent child]
|
rlm@10
|
531 (File. ^File (as-file parent) ^String (relative-path-string child)))
|
rlm@10
|
532 ([parent child & more]
|
rlm@10
|
533 (reduce file (file parent child) more)))
|
rlm@10
|
534
|
rlm@10
|
535 (defn delete-file
|
rlm@10
|
536 "Delete file f. Raise an exception if it fails unless silently is true."
|
rlm@10
|
537 [f & [silently]]
|
rlm@10
|
538 (or (.delete (file f))
|
rlm@10
|
539 silently
|
rlm@10
|
540 (throw (java.io.IOException. (str "Couldn't delete " f)))))
|
rlm@10
|
541
|
rlm@10
|
542 (defn delete-file-recursively
|
rlm@10
|
543 "Delete file f. If it's a directory, recursively delete all its contents.
|
rlm@10
|
544 Raise an exception if any deletion fails unless silently is true."
|
rlm@10
|
545 [f & [silently]]
|
rlm@10
|
546 (let [f (file f)]
|
rlm@10
|
547 (if (.isDirectory f)
|
rlm@10
|
548 (doseq [child (.listFiles f)]
|
rlm@10
|
549 (delete-file-recursively child silently)))
|
rlm@10
|
550 (delete-file f silently)))
|
rlm@10
|
551
|
rlm@10
|
552 (defmulti
|
rlm@10
|
553 ^{:deprecated "1.2"
|
rlm@10
|
554 :doc "Coerces argument (URL, URI, or String) to a java.net.URL."
|
rlm@10
|
555 :arglists '([arg])}
|
rlm@10
|
556 as-url type)
|
rlm@10
|
557
|
rlm@10
|
558 (defmethod as-url URL [x] x)
|
rlm@10
|
559
|
rlm@10
|
560 (defmethod as-url URI [^URI x] (.toURL x))
|
rlm@10
|
561
|
rlm@10
|
562 (defmethod as-url String [^String x] (URL. x))
|
rlm@10
|
563
|
rlm@10
|
564 (defmethod as-url File [^File x] (.toURL x))
|