1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
|
;;; duck_streams.clj -- duck-typed I/O streams for Clojure
;; by Stuart Sierra <mail@stuartsierra.com>
;; April 8, 2008
;; Copyright (c) 2008 Stuart Sierra. All rights reserved. The use and
;; distribution terms for this software are covered by the Common
;; Public License 1.0 (http://www.opensource.org/licenses/cpl1.0.php)
;; which can be found in the file CPL.TXT at the root of this
;; distribution. By using this software in any fashion, you are
;; agreeing to be bound by the terms of this license. You must not
;; remove this notice, or any other, from this software.
;; This file defines "duck-typed" I/O utility functions for Clojure.
;; The 'reader' and 'writer' functions will open and return an
;; instance of java.io.BufferedReader and java.io.PrintWriter,
;; respectively, for a variety of argument types -- filenames as
;; strings, URLs, java.io.File's, etc. These functions are not very
;; efficient, because they have to perform a number of 'instance?'
;; checks, but they are convenient when you just want to open a file
;; and don't want to deal with all the Java I/O classes.
;;
;; This file also defines two convenience functions, 'spit' (opposite
;; of 'slurp') and 'write-lines' (opposite of 'line-seq').
(clojure/ns clojure.contrib.duck-streams
(:import
(java.io Reader InputStream InputStreamReader FileReader
BufferedReader File PrintWriter OutputStream
OutputStreamWriter BufferedWriter Writer FileWriter)
(java.net URI URL MalformedURLException)))
(defmacro bufr
{:private true}
[reader]
`(new java.io.BufferedReader ~reader))
(defn reader
"Attempts to coerce its argument into an open
java.io.BufferedReader. Argument may be an instance of Reader,
BufferedReader, InputStream, File, URI, URL, or String.
If argument is a String, it tries to resolve it first as a URI, then
as a local file name. URIs with a 'file' protocol are converted to
local file names.
Should be used inside with-open to ensure the Reader is properly
closed."
[x]
(cond
(instance? BufferedReader x) x
(instance? Reader x) (bufr x)
(instance? InputStream x) (bufr (new InputStreamReader x))
(instance? File x) (bufr (new FileReader #^File x))
(instance? URL x) (if (= (. #^URL x (getProtocol)) "file")
(bufr (new FileReader (. #^URL x (getPath))))
(bufr (new InputStreamReader (. #^URL x (openStream)))))
(instance? URI x) (reader (. #^URI x (toURL)))
(instance? String x) (try (let [url (new URL x)]
(reader url))
(catch MalformedURLException err
(bufr (new FileReader #^String x))))
:else (throw (new Exception (str "Cannot coerce " (class x)
" into a Reader.")))))
(defmacro bufw
{:private true}
[writer]
`(new java.io.PrintWriter (new java.io.BufferedWriter ~writer)))
(defn writer
"Attempts to coerce its argument into an open java.io.PrintWriter
wrapped around a java.io.BufferedWriter. Argument may be an
instance of Writer, PrintWriter, BufferedWriter, OutputStream, File,
URI, URL, or String.
If argument is a String, it tries to resolve it first as a URI, then
as a local file name. URIs with a 'file' protocol are converted to
local file names.
Should be used inside with-open to ensure the Writer is properly
closed."
[x]
(cond
(instance? PrintWriter x) x
(instance? BufferedWriter x) (new PrintWriter #^BufferedWriter x)
(instance? Writer x) (bufw x) ; includes FileWriter
(instance? OutputStream x) (bufw (new OutputStreamWriter x))
(instance? File x) (bufw (new FileWriter #^File x))
(instance? URL x) (if (= (. #^URL x (getProtocol)) "file")
(bufw (new FileWriter (. #^URL x (getPath))))
(throw (new Exception (str "Cannot write to non-file URL <" x ">."))))
(instance? URI x) (writer (. #^URI x (toURL)))
(instance? String x) (try (let [url (new URL x)]
(writer url))
(catch MalformedURLException err
(bufw (new FileWriter #^String x))))
:else (throw (new Exception (str "Cannot coerce " (class x)
" into a Writer.")))))
(defn write-lines
"Opposite of 'line-seq'. Writes lines (a seq) to writer (an open
java.io.PrintWriter), separated by newlines."
[#^PrintWriter writer lines]
(let [line (first lines)]
(when line
(. writer (write (str line)))
(. writer (println))
(recur writer (rest lines)))))
(defn spit
"Opposite of 'slurp'. Writes 'contents' to the file named by
'filename'."
[filename contents]
(with-open w (#^PrintWriter writer filename)
(. w (print contents))))
|