diff src/clojure/contrib/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 diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/clojure/contrib/io.clj	Sat Aug 21 06:25:44 2010 -0400
     1.3 @@ -0,0 +1,564 @@
     1.4 +;;; io.clj -- duck-typed I/O streams for Clojure
     1.5 +
     1.6 +;; by Stuart Sierra, http://stuartsierra.com/
     1.7 +;; May 13, 2009
     1.8 +
     1.9 +;; Copyright (c) Stuart Sierra, 2009. All rights reserved.  The use
    1.10 +;; and distribution terms for this software are covered by the Eclipse
    1.11 +;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
    1.12 +;; which can be found in the file epl-v10.html at the root of this
    1.13 +;; distribution.  By using this software in any fashion, you are
    1.14 +;; agreeing to be bound by the terms of this license.  You must not
    1.15 +;; remove this notice, or any other, from this software.
    1.16 +
    1.17 +
    1.18 +;; This file defines "duck-typed" I/O utility functions for Clojure.
    1.19 +;; The 'reader' and 'writer' functions will open and return an
    1.20 +;; instance of java.io.BufferedReader and java.io.BufferedWriter,
    1.21 +;; respectively, for a variety of argument types -- filenames as
    1.22 +;; strings, URLs, java.io.File's, etc.  'reader' even works on http
    1.23 +;; URLs.
    1.24 +;;
    1.25 +;; Note: this is not really "duck typing" as implemented in languages
    1.26 +;; like Ruby.  A better name would have been "do-what-I-mean-streams"
    1.27 +;; or "just-give-me-a-stream", but ducks are funnier.
    1.28 +
    1.29 +
    1.30 +;; CHANGE LOG
    1.31 +;;
    1.32 +;; July 23, 2010: Most functions here are deprecated. Use
    1.33 +;; clojure.java.io
    1.34 +;;
    1.35 +;; May 13, 2009: added functions to open writers for appending
    1.36 +;;
    1.37 +;; May 3, 2009: renamed file to file-str, for compatibility with
    1.38 +;; clojure.contrib.java.  reader/writer no longer use this
    1.39 +;; function.
    1.40 +;;
    1.41 +;; February 16, 2009: (lazy branch) fixed read-lines to work with lazy
    1.42 +;; Clojure.
    1.43 +;;
    1.44 +;; January 10, 2009: added *default-encoding*, so streams are always
    1.45 +;; opened as UTF-8.
    1.46 +;;
    1.47 +;; December 19, 2008: rewrote reader and writer as multimethods; added
    1.48 +;; slurp*, file, and read-lines
    1.49 +;;
    1.50 +;; April 8, 2008: first version
    1.51 +
    1.52 +
    1.53 +
    1.54 +(ns 
    1.55 +  ^{:author "Stuart Sierra",
    1.56 +     :doc "This file defines polymorphic I/O utility functions for Clojure.
    1.57 +
    1.58 +           The Streams protocol defines reader, writer, input-stream and
    1.59 +           output-stream methods that return BufferedReader, BufferedWriter,
    1.60 +           BufferedInputStream and BufferedOutputStream instances (respectively),
    1.61 +           with default implementations extended to a variety of argument
    1.62 +           types: URLs or filenames as strings, java.io.File's, Sockets, etc."}
    1.63 +  clojure.contrib.io
    1.64 +  (:refer-clojure :exclude (spit))
    1.65 +  (:import 
    1.66 +   (java.io Reader InputStream InputStreamReader PushbackReader
    1.67 +            BufferedReader File OutputStream
    1.68 +            OutputStreamWriter BufferedWriter Writer
    1.69 +            FileInputStream FileOutputStream ByteArrayOutputStream
    1.70 +            StringReader ByteArrayInputStream
    1.71 +            BufferedInputStream BufferedOutputStream
    1.72 +            CharArrayReader)
    1.73 +   (java.net URI URL MalformedURLException Socket)))
    1.74 +
    1.75 +
    1.76 +(def
    1.77 + ^{:doc "Name of the default encoding to use when reading & writing.
    1.78 +  Default is UTF-8."
    1.79 +    :tag "java.lang.String"}
    1.80 + *default-encoding* "UTF-8")
    1.81 +
    1.82 +(def
    1.83 + ^{:doc "Size, in bytes or characters, of the buffer used when
    1.84 +  copying streams."}
    1.85 + *buffer-size* 1024)
    1.86 +
    1.87 +(def
    1.88 + ^{:doc "Type object for a Java primitive byte array."}
    1.89 + *byte-array-type* (class (make-array Byte/TYPE 0)))
    1.90 +
    1.91 +(def
    1.92 + ^{:doc "Type object for a Java primitive char array."}
    1.93 + *char-array-type* (class (make-array Character/TYPE 0)))
    1.94 +
    1.95 +
    1.96 +(defn ^File file-str
    1.97 +  "Concatenates args as strings and returns a java.io.File.  Replaces
    1.98 +  all / and \\ with File/separatorChar.  Replaces ~ at the start of
    1.99 +  the path with the user.home system property."
   1.100 +  [& args]
   1.101 +  (let [^String s (apply str args)
   1.102 +        s (.replace s \\ File/separatorChar)
   1.103 +        s (.replace s \/ File/separatorChar)
   1.104 +        s (if (.startsWith s "~")
   1.105 +            (str (System/getProperty "user.home")
   1.106 +                 File/separator (subs s 1))
   1.107 +            s)]
   1.108 +    (File. s)))
   1.109 +
   1.110 +(def
   1.111 + ^{:doc "If true, writer, output-stream and spit will open files in append mode.
   1.112 +          Defaults to false.  Instead of binding this var directly, use append-writer,
   1.113 +          append-output-stream or append-spit."
   1.114 +    :tag "java.lang.Boolean"}
   1.115 + *append* false)
   1.116 +
   1.117 +(defn- assert-not-appending []
   1.118 +  (when *append*
   1.119 +    (throw (Exception. "Cannot change an open stream to append mode."))))
   1.120 +
   1.121 +;; @todo -- Both simple and elaborate methods for controlling buffering of
   1.122 +;; in the Streams protocol were implemented, considered, and postponed
   1.123 +;; see http://groups.google.com/group/clojure-dev/browse_frm/thread/3e39e9b3982f542b
   1.124 +(defprotocol Streams
   1.125 +  (reader [x]
   1.126 +    "Attempts to coerce its argument into an open java.io.Reader.
   1.127 +     The default implementations of this protocol always return a
   1.128 +     java.io.BufferedReader.
   1.129 +
   1.130 +     Default implementations are provided for Reader, BufferedReader,
   1.131 +     InputStream, File, URI, URL, Socket, byte arrays, character arrays,
   1.132 +     and String.
   1.133 +
   1.134 +     If argument is a String, it tries to resolve it first as a URI, then
   1.135 +     as a local file name.  URIs with a 'file' protocol are converted to
   1.136 +     local file names.  If this fails, a final attempt is made to resolve
   1.137 +     the string as a resource on the CLASSPATH.
   1.138 +
   1.139 +     Uses *default-encoding* as the text encoding.
   1.140 +
   1.141 +     Should be used inside with-open to ensure the Reader is properly
   1.142 +     closed.")
   1.143 +  (writer [x]
   1.144 +    "Attempts to coerce its argument into an open java.io.Writer.
   1.145 +     The default implementations of this protocol always return a
   1.146 +     java.io.BufferedWriter.
   1.147 +
   1.148 +     Default implementations are provided for Writer, BufferedWriter,
   1.149 +     OutputStream, File, URI, URL, Socket, and String.
   1.150 +
   1.151 +     If the argument is a String, it tries to resolve it first as a URI, then
   1.152 +     as a local file name.  URIs with a 'file' protocol are converted to
   1.153 +     local file names.
   1.154 +
   1.155 +     Should be used inside with-open to ensure the Writer is properly
   1.156 +     closed.")
   1.157 +  (input-stream [x]
   1.158 +    "Attempts to coerce its argument into an open java.io.InputStream.
   1.159 +     The default implementations of this protocol always return a
   1.160 +     java.io.BufferedInputStream.
   1.161 +
   1.162 +     Default implementations are defined for OutputStream, File, URI, URL,
   1.163 +     Socket, byte array, and String arguments.
   1.164 +
   1.165 +     If the argument is a String, it tries to resolve it first as a URI, then
   1.166 +     as a local file name.  URIs with a 'file' protocol are converted to
   1.167 +     local file names.
   1.168 +
   1.169 +     Should be used inside with-open to ensure the InputStream is properly
   1.170 +     closed.")
   1.171 +  (output-stream [x]
   1.172 +    "Attempts to coerce its argument into an open java.io.OutputStream.
   1.173 +     The default implementations of this protocol always return a
   1.174 +     java.io.BufferedOutputStream.
   1.175 +
   1.176 +     Default implementations are defined for OutputStream, File, URI, URL,
   1.177 +     Socket, and String arguments.
   1.178 +
   1.179 +     If the argument is a String, it tries to resolve it first as a URI, then
   1.180 +     as a local file name.  URIs with a 'file' protocol are converted to
   1.181 +     local file names.
   1.182 +
   1.183 +     Should be used inside with-open to ensure the OutputStream is
   1.184 +     properly closed."))
   1.185 +
   1.186 +(def default-streams-impl
   1.187 +  {:reader #(reader (input-stream %))
   1.188 +   :writer #(writer (output-stream %))
   1.189 +   :input-stream #(throw (Exception. (str "Cannot open <" (pr-str %) "> as an InputStream.")))
   1.190 +   :output-stream #(throw (Exception. (str "Cannot open <" (pr-str %) "> as an OutputStream.")))})
   1.191 +
   1.192 +(extend File
   1.193 +  Streams
   1.194 +  (assoc default-streams-impl
   1.195 +    :input-stream #(input-stream (FileInputStream. ^File %))
   1.196 +    :output-stream #(let [stream (FileOutputStream. ^File % *append*)]
   1.197 +                      (binding [*append* false]
   1.198 +                        (output-stream stream)))))
   1.199 +(extend URL
   1.200 +  Streams
   1.201 +  (assoc default-streams-impl
   1.202 +    :input-stream (fn [^URL x]
   1.203 +                    (input-stream (if (= "file" (.getProtocol x))
   1.204 +                                    (FileInputStream. (.getPath x))
   1.205 +                                    (.openStream x))))
   1.206 +    :output-stream (fn [^URL x]
   1.207 +                     (if (= "file" (.getProtocol x))
   1.208 +                       (output-stream (File. (.getPath x)))
   1.209 +                       (throw (Exception. (str "Can not write to non-file URL <" x ">")))))))
   1.210 +(extend URI
   1.211 +  Streams
   1.212 +  (assoc default-streams-impl
   1.213 +    :input-stream #(input-stream (.toURL ^URI %))
   1.214 +    :output-stream #(output-stream (.toURL ^URI %))))
   1.215 +(extend String
   1.216 +  Streams
   1.217 +  (assoc default-streams-impl
   1.218 +    :input-stream #(try
   1.219 +                     (input-stream (URL. %))
   1.220 +                     (catch MalformedURLException e
   1.221 +                       (input-stream (File. ^String %))))
   1.222 +    :output-stream #(try
   1.223 +                      (output-stream (URL. %))
   1.224 +                      (catch MalformedURLException err
   1.225 +                        (output-stream (File. ^String %))))))
   1.226 +(extend Socket
   1.227 +  Streams
   1.228 +  (assoc default-streams-impl
   1.229 +    :input-stream #(.getInputStream ^Socket %)
   1.230 +    :output-stream #(output-stream (.getOutputStream ^Socket %))))
   1.231 +(extend *byte-array-type*
   1.232 +  Streams
   1.233 +  (assoc default-streams-impl :input-stream #(input-stream (ByteArrayInputStream. %))))
   1.234 +(extend *char-array-type*
   1.235 +  Streams
   1.236 +  (assoc default-streams-impl :reader #(reader (CharArrayReader. %))))
   1.237 +(extend Object
   1.238 +  Streams
   1.239 +  default-streams-impl)
   1.240 +
   1.241 +(extend Reader
   1.242 +  Streams
   1.243 +  (assoc default-streams-impl :reader #(BufferedReader. %)))
   1.244 +(extend BufferedReader
   1.245 +  Streams
   1.246 +  (assoc default-streams-impl :reader identity))
   1.247 +(defn- inputstream->reader
   1.248 +  [^InputStream is]
   1.249 +  (reader (InputStreamReader. is *default-encoding*)))
   1.250 +(extend InputStream
   1.251 +  Streams
   1.252 +  (assoc default-streams-impl :input-stream #(BufferedInputStream. %)
   1.253 +    :reader inputstream->reader))
   1.254 +(extend BufferedInputStream
   1.255 +  Streams
   1.256 +  (assoc default-streams-impl
   1.257 +    :input-stream identity
   1.258 +    :reader inputstream->reader))
   1.259 +
   1.260 +(extend Writer
   1.261 +  Streams
   1.262 +  (assoc default-streams-impl :writer #(do (assert-not-appending)
   1.263 +                                           (BufferedWriter. %))))
   1.264 +(extend BufferedWriter
   1.265 +  Streams
   1.266 +  (assoc default-streams-impl :writer #(do (assert-not-appending) %)))
   1.267 +(defn- outputstream->writer
   1.268 +  [^OutputStream os]
   1.269 +  (assert-not-appending)
   1.270 +  (writer (OutputStreamWriter. os *default-encoding*)))
   1.271 +(extend OutputStream
   1.272 +  Streams
   1.273 +  (assoc default-streams-impl
   1.274 +    :output-stream #(do (assert-not-appending)
   1.275 +                        (BufferedOutputStream. %))
   1.276 +    :writer outputstream->writer))
   1.277 +(extend BufferedOutputStream
   1.278 +  Streams
   1.279 +  (assoc default-streams-impl
   1.280 +    :output-stream #(do (assert-not-appending) %)
   1.281 +      :writer outputstream->writer))
   1.282 +
   1.283 +(defn append-output-stream
   1.284 +  "Like output-stream but opens file for appending.  Does not work on streams
   1.285 +  that are already open."
   1.286 +  {:deprecated "1.2"}
   1.287 +  [x]
   1.288 +  (binding [*append* true]
   1.289 +    (output-stream x)))
   1.290 +
   1.291 +(defn append-writer
   1.292 +  "Like writer but opens file for appending.  Does not work on streams
   1.293 +  that are already open."
   1.294 +  {:deprecated "1.2"}
   1.295 +  [x]
   1.296 +  (binding [*append* true]
   1.297 +    (writer x)))
   1.298 +
   1.299 +(defn write-lines
   1.300 +  "Writes lines (a seq) to f, separated by newlines.  f is opened with
   1.301 +  writer, and automatically closed at the end of the sequence."
   1.302 +  [f lines]
   1.303 +  (with-open [^BufferedWriter writer (writer f)]
   1.304 +    (loop [lines lines]
   1.305 +      (when-let [line (first lines)]
   1.306 +        (.write writer (str line))
   1.307 +        (.newLine writer)
   1.308 +        (recur (rest lines))))))
   1.309 +
   1.310 +(defn read-lines
   1.311 +  "Like clojure.core/line-seq but opens f with reader.  Automatically
   1.312 +  closes the reader AFTER YOU CONSUME THE ENTIRE SEQUENCE."
   1.313 +  [f]
   1.314 +  (let [read-line (fn this [^BufferedReader rdr]
   1.315 +                    (lazy-seq
   1.316 +                     (if-let [line (.readLine rdr)]
   1.317 +                       (cons line (this rdr))
   1.318 +                       (.close rdr))))]
   1.319 +    (read-line (reader f))))
   1.320 +
   1.321 +(defn ^String slurp*
   1.322 +  "Like clojure.core/slurp but opens f with reader."
   1.323 +  {:deprecated "1.2"}
   1.324 +  [f]
   1.325 +  (with-open [^BufferedReader r (reader f)]
   1.326 +      (let [sb (StringBuilder.)]
   1.327 +        (loop [c (.read r)]
   1.328 +          (if (neg? c)
   1.329 +            (str sb)
   1.330 +            (do (.append sb (char c))
   1.331 +                (recur (.read r))))))))
   1.332 +
   1.333 +(defn spit
   1.334 +  "Opposite of slurp.  Opens f with writer, writes content, then
   1.335 +  closes f."
   1.336 +  {:deprecated "1.2"}
   1.337 +  [f content]
   1.338 +  (with-open [^Writer w (writer f)]
   1.339 +    (.write w content)))
   1.340 +
   1.341 +(defn append-spit
   1.342 +  "Like spit but appends to file."
   1.343 +  {:deprecated "1.2"}
   1.344 +  [f content]
   1.345 +  (with-open [^Writer w (append-writer f)]
   1.346 +    (.write w content)))
   1.347 +
   1.348 +(defn pwd
   1.349 +  "Returns current working directory as a String.  (Like UNIX 'pwd'.)
   1.350 +  Note: In Java, you cannot change the current working directory."
   1.351 +  {:deprecated "1.2"}
   1.352 +  []
   1.353 +  (System/getProperty "user.dir"))
   1.354 +
   1.355 +(defmacro with-out-writer
   1.356 +  "Opens a writer on f, binds it to *out*, and evalutes body.
   1.357 +  Anything printed within body will be written to f."
   1.358 +  [f & body]
   1.359 +  `(with-open [stream# (writer ~f)]
   1.360 +     (binding [*out* stream#]
   1.361 +       ~@body)))
   1.362 +
   1.363 +(defmacro with-out-append-writer
   1.364 +  "Like with-out-writer but appends to file."
   1.365 +  {:deprecated "1.2"}
   1.366 +  [f & body]
   1.367 +  `(with-open [stream# (append-writer ~f)]
   1.368 +     (binding [*out* stream#]
   1.369 +       ~@body)))
   1.370 +
   1.371 +(defmacro with-in-reader
   1.372 +  "Opens a PushbackReader on f, binds it to *in*, and evaluates body."
   1.373 +  [f & body]
   1.374 +  `(with-open [stream# (PushbackReader. (reader ~f))]
   1.375 +     (binding [*in* stream#]
   1.376 +       ~@body)))
   1.377 +
   1.378 +(defmulti
   1.379 +  ^{:deprecated "1.2"
   1.380 +    :doc "Copies input to output.  Returns nil.
   1.381 +  Input may be an InputStream, Reader, File, byte[], or String.
   1.382 +  Output may be an OutputStream, Writer, or File.
   1.383 +
   1.384 +  Does not close any streams except those it opens itself 
   1.385 +  (on a File).
   1.386 +
   1.387 +  Writing a File fails if the parent directory does not exist."
   1.388 +     :arglists '([input output])}
   1.389 +  copy
   1.390 +  (fn [input output] [(type input) (type output)]))
   1.391 +
   1.392 +(defmethod copy [InputStream OutputStream] [^InputStream input ^OutputStream output]
   1.393 +  (let [buffer (make-array Byte/TYPE *buffer-size*)]
   1.394 +    (loop []
   1.395 +      (let [size (.read input buffer)]
   1.396 +        (when (pos? size)
   1.397 +          (do (.write output buffer 0 size)
   1.398 +              (recur)))))))
   1.399 +
   1.400 +(defmethod copy [InputStream Writer] [^InputStream input ^Writer output]
   1.401 +  (let [^"[B" buffer (make-array Byte/TYPE *buffer-size*)]
   1.402 +    (loop []
   1.403 +      (let [size (.read input buffer)]
   1.404 +        (when (pos? size)
   1.405 +          (let [chars (.toCharArray (String. buffer 0 size *default-encoding*))]
   1.406 +            (do (.write output chars)
   1.407 +                (recur))))))))
   1.408 +
   1.409 +(defmethod copy [InputStream File] [^InputStream input ^File output]
   1.410 +  (with-open [out (FileOutputStream. output)]
   1.411 +    (copy input out)))
   1.412 +
   1.413 +(defmethod copy [Reader OutputStream] [^Reader input ^OutputStream output]
   1.414 +  (let [^"[C" buffer (make-array Character/TYPE *buffer-size*)]
   1.415 +    (loop []
   1.416 +      (let [size (.read input buffer)]
   1.417 +        (when (pos? size)
   1.418 +          (let [bytes (.getBytes (String. buffer 0 size) *default-encoding*)]
   1.419 +            (do (.write output bytes)
   1.420 +                (recur))))))))
   1.421 +
   1.422 +(defmethod copy [Reader Writer] [^Reader input ^Writer output]
   1.423 +  (let [^"[C" buffer (make-array Character/TYPE *buffer-size*)]
   1.424 +    (loop []
   1.425 +      (let [size (.read input buffer)]
   1.426 +        (when (pos? size)
   1.427 +          (do (.write output buffer 0 size)
   1.428 +              (recur)))))))
   1.429 +
   1.430 +(defmethod copy [Reader File] [^Reader input ^File output]
   1.431 +  (with-open [out (FileOutputStream. output)]
   1.432 +    (copy input out)))
   1.433 +
   1.434 +(defmethod copy [File OutputStream] [^File input ^OutputStream output]
   1.435 +  (with-open [in (FileInputStream. input)]
   1.436 +    (copy in output)))
   1.437 +
   1.438 +(defmethod copy [File Writer] [^File input ^Writer output]
   1.439 +  (with-open [in (FileInputStream. input)]
   1.440 +    (copy in output)))
   1.441 +
   1.442 +(defmethod copy [File File] [^File input ^File output]
   1.443 +  (with-open [in (FileInputStream. input)
   1.444 +              out (FileOutputStream. output)]
   1.445 +    (copy in out)))
   1.446 +
   1.447 +(defmethod copy [String OutputStream] [^String input ^OutputStream output]
   1.448 +  (copy (StringReader. input) output))
   1.449 +
   1.450 +(defmethod copy [String Writer] [^String input ^Writer output]
   1.451 +  (copy (StringReader. input) output))
   1.452 +
   1.453 +(defmethod copy [String File] [^String input ^File output]
   1.454 +  (copy (StringReader. input) output))
   1.455 +
   1.456 +(defmethod copy [*char-array-type* OutputStream] [input ^OutputStream output]
   1.457 +  (copy (CharArrayReader. input) output))
   1.458 +
   1.459 +(defmethod copy [*char-array-type* Writer] [input ^Writer output]
   1.460 +  (copy (CharArrayReader. input) output))
   1.461 +
   1.462 +(defmethod copy [*char-array-type* File] [input ^File output]
   1.463 +  (copy (CharArrayReader. input) output))
   1.464 +
   1.465 +(defmethod copy [*byte-array-type* OutputStream] [^"[B" input ^OutputStream output]
   1.466 +  (copy (ByteArrayInputStream. input) output))
   1.467 +
   1.468 +(defmethod copy [*byte-array-type* Writer] [^"[B" input ^Writer output]
   1.469 +  (copy (ByteArrayInputStream. input) output))
   1.470 +
   1.471 +(defmethod copy [*byte-array-type* File] [^"[B" input ^Writer output]
   1.472 +  (copy (ByteArrayInputStream. input) output))
   1.473 +
   1.474 +(defn make-parents
   1.475 +  "Creates all parent directories of file."
   1.476 +  [^File file]
   1.477 +  (.mkdirs (.getParentFile file)))
   1.478 +
   1.479 +(defmulti
   1.480 +  ^{:doc "Converts argument into a Java byte array.  Argument may be
   1.481 +  a String, File, InputStream, or Reader.  If the argument is already
   1.482 +  a byte array, returns it."
   1.483 +    :arglists '([arg])}
   1.484 +  to-byte-array type)
   1.485 +
   1.486 +(defmethod to-byte-array *byte-array-type* [x] x)
   1.487 +
   1.488 +(defmethod to-byte-array String [^String x]
   1.489 +  (.getBytes x *default-encoding*))
   1.490 +
   1.491 +(defmethod to-byte-array File [^File x]
   1.492 +  (with-open [input (FileInputStream. x)
   1.493 +              buffer (ByteArrayOutputStream.)]
   1.494 +    (copy input buffer)
   1.495 +    (.toByteArray buffer)))
   1.496 +
   1.497 +(defmethod to-byte-array InputStream [^InputStream x]
   1.498 +  (let [buffer (ByteArrayOutputStream.)]
   1.499 +    (copy x buffer)
   1.500 +    (.toByteArray buffer)))
   1.501 +
   1.502 +(defmethod to-byte-array Reader [^Reader x]
   1.503 +  (.getBytes (slurp* x) *default-encoding*))
   1.504 +
   1.505 +(defmulti relative-path-string 
   1.506 +  "Interpret a String or java.io.File as a relative path string. 
   1.507 +   Building block for clojure.contrib.java/file."
   1.508 +  {:deprecated "1.2"}
   1.509 +  class)
   1.510 +
   1.511 +(defmethod relative-path-string String [^String s]
   1.512 +  (relative-path-string (File. s)))
   1.513 +
   1.514 +(defmethod relative-path-string File [^File f]
   1.515 +  (if (.isAbsolute f)
   1.516 +    (throw (IllegalArgumentException. (str f " is not a relative path")))
   1.517 +    (.getPath f)))
   1.518 +
   1.519 +(defmulti ^File as-file 
   1.520 +  "Interpret a String or a java.io.File as a File. Building block
   1.521 +   for clojure.contrib.java/file, which you should prefer
   1.522 +   in most cases."
   1.523 +  {:deprecated "1.2"}
   1.524 +  class)
   1.525 +(defmethod as-file String [^String s] (File. s))
   1.526 +(defmethod as-file File [f] f)
   1.527 +
   1.528 +(defn ^File file
   1.529 +  "Returns a java.io.File from string or file args."
   1.530 +  {:deprecated "1.2"}
   1.531 +  ([arg]                      
   1.532 +     (as-file arg))
   1.533 +  ([parent child]             
   1.534 +     (File. ^File (as-file parent) ^String (relative-path-string child)))
   1.535 +  ([parent child & more]
   1.536 +     (reduce file (file parent child) more)))
   1.537 +
   1.538 +(defn delete-file
   1.539 +  "Delete file f. Raise an exception if it fails unless silently is true."
   1.540 +  [f & [silently]]
   1.541 +  (or (.delete (file f))
   1.542 +      silently
   1.543 +      (throw (java.io.IOException. (str "Couldn't delete " f)))))
   1.544 +
   1.545 +(defn delete-file-recursively
   1.546 +  "Delete file f. If it's a directory, recursively delete all its contents.
   1.547 +Raise an exception if any deletion fails unless silently is true."
   1.548 +  [f & [silently]]
   1.549 +  (let [f (file f)]
   1.550 +    (if (.isDirectory f)
   1.551 +      (doseq [child (.listFiles f)]
   1.552 +        (delete-file-recursively child silently)))
   1.553 +    (delete-file f silently)))
   1.554 +
   1.555 +(defmulti
   1.556 +  ^{:deprecated "1.2"
   1.557 +    :doc "Coerces argument (URL, URI, or String) to a java.net.URL."
   1.558 +    :arglists '([arg])}
   1.559 +  as-url type)
   1.560 +
   1.561 +(defmethod as-url URL [x] x)
   1.562 +
   1.563 +(defmethod as-url URI [^URI x] (.toURL x))
   1.564 +
   1.565 +(defmethod as-url String [^String x] (URL. x))
   1.566 +
   1.567 +(defmethod as-url File [^File x] (.toURL x))