summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/clj/clojure/core.clj50
-rw-r--r--src/clj/clojure/java/io.clj429
2 files changed, 462 insertions, 17 deletions
diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj
index 4c466c56..0a2a39ae 100644
--- a/src/clj/clojure/core.clj
+++ b/src/clj/clojure/core.clj
@@ -3918,23 +3918,6 @@
{:added "1.0"}
[v] (instance? clojure.lang.Var v))
-(defn slurp
- "Reads the file named by f using the encoding enc into a string
- and returns it."
- {:added "1.0"}
- ([f] (slurp f (.name (java.nio.charset.Charset/defaultCharset))))
- ([^String f ^String enc]
- (with-open [r (new java.io.BufferedReader
- (new java.io.InputStreamReader
- (new java.io.FileInputStream f) enc))]
- (let [sb (new StringBuilder)]
- (loop [c (.read r)]
- (if (neg? c)
- (str sb)
- (do
- (.append sb (char c))
- (recur (.read r)))))))))
-
(defn subs
"Returns the substring of s beginning at start inclusive, and ending
at end (defaults to length of string), exclusive."
@@ -5338,6 +5321,39 @@
(let [s (seq coll)]
(clojure.core.protocols/internal-reduce s f val))))
+(require '[clojure.java.io :as jio])
+
+(defn- normalize-slurp-opts
+ [opts]
+ (if (string? (first opts))
+ (do
+ (println "WARNING: (slurp f enc) is deprecated, use (slurp f :encoding enc).")
+ [:encoding (first opts)])
+ opts))
+
+(defn slurp
+ "Reads the file named by f using the encoding enc into a string
+ and returns it."
+ {:added "1.0"}
+ ([f & opts]
+ (let [opts (normalize-slurp-opts opts)
+ sb (StringBuilder.)]
+ (with-open [#^java.io.Reader r (apply jio/reader f opts)]
+ (loop [c (.read r)]
+ (if (neg? c)
+ (str sb)
+ (do
+ (.append sb (char c))
+ (recur (.read r)))))))))
+
+(defn spit
+ "Opposite of slurp. Opens f with writer, writes content, then
+ closes f."
+ {:added "1.2"}
+ [f content & options]
+ (with-open [#^java.io.Writer w (apply jio/writer f options)]
+ (.write w content)))
+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; futures (needs proxy);;;;;;;;;;;;;;;;;;
(defn future-call
"Takes a function of no args and yields a future object that will
diff --git a/src/clj/clojure/java/io.clj b/src/clj/clojure/java/io.clj
new file mode 100644
index 00000000..bed18bc9
--- /dev/null
+++ b/src/clj/clojure/java/io.clj
@@ -0,0 +1,429 @@
+; Copyright (c) Rich Hickey. All rights reserved.
+; The use and distribution terms for this software are covered by the
+; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+; which can be found in the file epl-v10.html 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.
+
+(ns
+ ^{:author "Stuart Sierra, Chas Emerick, Stuart Halloway",
+ :doc "This file defines polymorphic I/O utility functions for Clojure."}
+ clojure.java.io
+ (:import
+ (java.io Reader InputStream InputStreamReader PushbackReader
+ BufferedReader File OutputStream
+ OutputStreamWriter BufferedWriter Writer
+ FileInputStream FileOutputStream ByteArrayOutputStream
+ StringReader ByteArrayInputStream
+ BufferedInputStream BufferedOutputStream
+ CharArrayReader Closeable)
+ (java.net URI URL MalformedURLException Socket)))
+
+(def
+ ^{:doc "Type object for a Java primitive byte array."
+ :private true
+ }
+ byte-array-type (class (make-array Byte/TYPE 0)))
+
+(def
+ ^{:doc "Type object for a Java primitive char array."
+ :private true}
+ char-array-type (class (make-array Character/TYPE 0)))
+
+(defprotocol ^{:added "1.2"} Coercions
+ "Coerce between various 'resource-namish' things."
+ (as-file [x] "Coerce argument to a file.")
+ (as-url [x] "Coerce argument to a URL."))
+
+(extend-protocol Coercions
+ nil
+ (as-file [_] nil)
+ (as-url [_] nil)
+
+ String
+ (as-file [s] (File. s))
+ (as-url [s] (URL. s))
+
+ File
+ (as-file [f] f)
+ (as-url [f] (.toURL f))
+
+ URL
+ (as-url [u] u)
+ (as-file [u]
+ (if (= "file" (.getProtocol u))
+ (as-file (.getPath u))
+ (throw (IllegalArgumentException. "Not a file: " u))))
+
+ URI
+ (as-url [u] (.toURL u))
+ (as-file [u] (as-file (as-url u))))
+
+(defprotocol IOFactory
+ (make-reader [x opts])
+ (make-writer [x opts])
+ (make-input-stream [x opts])
+ (make-output-stream [x opts]))
+
+(defn ^Reader reader
+ "Attempts to coerce its argument into an open java.io.Reader.
+ The default implementations of this protocol always return a
+ java.io.BufferedReader.
+
+ Default implementations are provided for Reader, BufferedReader,
+ InputStream, File, URI, URL, Socket, byte arrays, character arrays,
+ and 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."
+ {:added "1.2"}
+ [x & opts]
+ (make-reader x (when opts (apply hash-map opts))))
+
+(defn ^Writer writer
+ "Attempts to coerce its argument into an open java.io.Writer.
+ The default implementations of this protocol always return a
+ java.io.BufferedWriter.
+
+ Default implementations are provided for Writer, BufferedWriter,
+ OutputStream, File, URI, URL, Socket, and String.
+
+ If the 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."
+ {:added "1.2"}
+ [x & opts]
+ (make-writer x (when opts (apply hash-map opts))))
+
+(defn ^InputStream input-stream
+ "Attempts to coerce its argument into an open java.io.InputStream.
+ The default implementations of this protocol always return a
+ java.io.BufferedInputStream.
+
+ Default implementations are defined for OutputStream, File, URI, URL,
+ Socket, byte array, and String arguments.
+
+ If the 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 InputStream is properly
+ closed."
+ {:added "1.2"}
+ [x & opts]
+ (make-input-stream x (when opts (apply hash-map opts))))
+
+(defn ^OutputStream output-stream
+ "Attempts to coerce its argument into an open java.io.OutputStream.
+ The default implementations of this protocol always return a
+ java.io.BufferedOutputStream.
+
+ Default implementations are defined for OutputStream, File, URI, URL,
+ Socket, and String arguments.
+
+ If the 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 OutputStream is
+ properly closed."
+ {:added "1.2"}
+ [x & opts]
+ (make-output-stream x (when opts (apply hash-map opts))))
+
+(defn- ^Boolean append? [opts]
+ (boolean (:append opts)))
+
+(defn- ^String encoding [opts]
+ (or (:encoding opts) "UTF-8"))
+
+(defn- buffer-size [opts]
+ (or (:buffer-size opts) 1024))
+
+(def default-streams-impl
+ {:make-reader (fn [x opts] (make-reader (make-input-stream x opts) opts))
+ :make-writer (fn [x opts] (make-writer (make-output-stream x opts) opts))
+ :make-input-stream (fn [x opts]
+ (throw (IllegalArgumentException.
+ (str "Cannot open <" (pr-str x) "> as an InputStream."))))
+ :make-output-stream (fn [x opts]
+ (throw (IllegalArgumentException.
+ (str "Cannot open <" (pr-str x) "> as an OutputStream."))))})
+
+(defn- inputstream->reader
+ [^InputStream is opts]
+ (make-reader (InputStreamReader. is (encoding opts)) opts))
+
+(defn- outputstream->writer
+ [^OutputStream os opts]
+ (make-writer (OutputStreamWriter. os (encoding opts)) opts))
+
+(extend BufferedInputStream
+ IOFactory
+ (assoc default-streams-impl
+ :make-input-stream (fn [x opts] x)
+ :make-reader inputstream->reader))
+
+(extend InputStream
+ IOFactory
+ (assoc default-streams-impl
+ :make-input-stream (fn [x opts] (BufferedInputStream. x))
+ :make-reader inputstream->reader))
+
+(extend Reader
+ IOFactory
+ (assoc default-streams-impl
+ :make-reader (fn [x opts] (BufferedReader. x))))
+
+(extend BufferedReader
+ IOFactory
+ (assoc default-streams-impl
+ :make-reader (fn [x opts] x)))
+
+(extend Writer
+ IOFactory
+ (assoc default-streams-impl
+ :make-writer (fn [x opts] (BufferedWriter. x))))
+
+(extend BufferedWriter
+ IOFactory
+ (assoc default-streams-impl
+ :make-writer (fn [x opts] x)))
+
+(extend OutputStream
+ IOFactory
+ (assoc default-streams-impl
+ :make-output-stream (fn [x opts] (BufferedOutputStream. x))
+ :make-writer outputstream->writer))
+
+(extend BufferedOutputStream
+ IOFactory
+ (assoc default-streams-impl
+ :make-output-stream (fn [x opts] x)
+ :make-writer outputstream->writer))
+
+(extend File
+ IOFactory
+ (assoc default-streams-impl
+ :make-input-stream (fn [^File x opts] (make-input-stream (FileInputStream. x) opts))
+ :make-output-stream (fn [^File x opts] (make-output-stream (FileOutputStream. x (append? opts)) opts))))
+
+(extend URL
+ IOFactory
+ (assoc default-streams-impl
+ :make-input-stream (fn [^URL x opts]
+ (make-input-stream
+ (if (= "file" (.getProtocol x))
+ (FileInputStream. (.getPath x))
+ (.openStream x)) opts))
+ :make-output-stream (fn [^URL x opts]
+ (if (= "file" (.getProtocol x))
+ (make-output-stream (File. (.getPath x)) opts)
+ (throw (IllegalArgumentException. (str "Can not write to non-file URL <" x ">")))))))
+
+(extend URI
+ IOFactory
+ (assoc default-streams-impl
+ :make-input-stream (fn [^URI x opts] (make-input-stream (.toURL x) opts))
+ :make-output-stream (fn [^URI x opts] (make-output-stream (.toURL x) opts))))
+
+(extend String
+ IOFactory
+ (assoc default-streams-impl
+ :make-input-stream (fn [^String x opts]
+ (try
+ (make-input-stream (URL. x) opts)
+ (catch MalformedURLException e
+ (make-input-stream (File. x) opts))))
+ :make-output-stream (fn [^String x opts]
+ (try
+ (make-output-stream (URL. x) opts)
+ (catch MalformedURLException err
+ (make-output-stream (File. x) opts))))))
+
+(extend Socket
+ IOFactory
+ (assoc default-streams-impl
+ :make-input-stream (fn [^Socket x opts] (.getInputStream x))
+ :output-stream (fn [^Socket x opts] (output-stream (.getOutputStream x) opts))))
+
+(extend byte-array-type
+ IOFactory
+ (assoc default-streams-impl
+ :make-input-stream (fn [x opts] (make-input-stream (ByteArrayInputStream. x) opts))))
+
+(extend char-array-type
+ IOFactory
+ (assoc default-streams-impl
+ :make-reader (fn [x opts] (make-reader (CharArrayReader. x) opts))))
+
+(extend Object
+ IOFactory
+ default-streams-impl)
+
+(defmulti
+ #^{:doc "Internal helper for copy"
+ :private true
+ :arglists '([input output opts])}
+ do-copy
+ (fn [input output opts] [(type input) (type output)]))
+
+(defmethod do-copy [InputStream OutputStream] [#^InputStream input #^OutputStream output opts]
+ (let [buffer (make-array Byte/TYPE (buffer-size opts))]
+ (loop []
+ (let [size (.read input buffer)]
+ (when (pos? size)
+ (do (.write output buffer 0 size)
+ (recur)))))))
+
+(defmethod do-copy [InputStream Writer] [#^InputStream input #^Writer output opts]
+ (let [#^"[B" buffer (make-array Byte/TYPE (buffer-size opts))]
+ (loop []
+ (let [size (.read input buffer)]
+ (when (pos? size)
+ (let [chars (.toCharArray (String. buffer 0 size (encoding opts)))]
+ (do (.write output chars)
+ (recur))))))))
+
+(defmethod do-copy [InputStream File] [#^InputStream input #^File output opts]
+ (with-open [out (FileOutputStream. output)]
+ (do-copy input out opts)))
+
+(defmethod do-copy [Reader OutputStream] [#^Reader input #^OutputStream output opts]
+ (let [#^"[C" buffer (make-array Character/TYPE (buffer-size opts))]
+ (loop []
+ (let [size (.read input buffer)]
+ (when (pos? size)
+ (let [bytes (.getBytes (String. buffer 0 size) (encoding opts))]
+ (do (.write output bytes)
+ (recur))))))))
+
+(defmethod do-copy [Reader Writer] [#^Reader input #^Writer output opts]
+ (let [#^"[C" buffer (make-array Character/TYPE (buffer-size opts))]
+ (loop []
+ (let [size (.read input buffer)]
+ (when (pos? size)
+ (do (.write output buffer 0 size)
+ (recur)))))))
+
+(defmethod do-copy [Reader File] [#^Reader input #^File output opts]
+ (with-open [out (FileOutputStream. output)]
+ (do-copy input out opts)))
+
+(defmethod do-copy [File OutputStream] [#^File input #^OutputStream output opts]
+ (with-open [in (FileInputStream. input)]
+ (do-copy in output opts)))
+
+(defmethod do-copy [File Writer] [#^File input #^Writer output opts]
+ (with-open [in (FileInputStream. input)]
+ (do-copy in output opts)))
+
+(defmethod do-copy [File File] [#^File input #^File output opts]
+ (with-open [in (FileInputStream. input)
+ out (FileOutputStream. output)]
+ (do-copy in out opts)))
+
+(defmethod do-copy [String OutputStream] [#^String input #^OutputStream output opts]
+ (do-copy (StringReader. input) output opts))
+
+(defmethod do-copy [String Writer] [#^String input #^Writer output opts]
+ (do-copy (StringReader. input) output opts))
+
+(defmethod do-copy [String File] [#^String input #^File output opts]
+ (do-copy (StringReader. input) output opts))
+
+(defmethod do-copy [char-array-type OutputStream] [input #^OutputStream output opts]
+ (do-copy (CharArrayReader. input) output opts))
+
+(defmethod do-copy [char-array-type Writer] [input #^Writer output opts]
+ (do-copy (CharArrayReader. input) output opts))
+
+(defmethod do-copy [char-array-type File] [input #^File output opts]
+ (do-copy (CharArrayReader. input) output opts))
+
+(defmethod do-copy [byte-array-type OutputStream] [#^"[B" input #^OutputStream output opts]
+ (do-copy (ByteArrayInputStream. input) output opts))
+
+(defmethod do-copy [byte-array-type Writer] [#^"[B" input #^Writer output opts]
+ (do-copy (ByteArrayInputStream. input) output opts))
+
+(defmethod do-copy [byte-array-type File] [#^"[B" input #^Writer output opts]
+ (do-copy (ByteArrayInputStream. input) output opts))
+
+(defn copy
+ "Copies input to output. Returns nil or throws IOException.
+ Input may be an InputStream, Reader, File, byte[], or String.
+ Output may be an OutputStream, Writer, or File.
+
+ Options are key/value pairs and may be one of
+
+ :buffer-size buffer size to use, default is 1024.
+ :encoding encoding to use if converting between
+ byte and char streams.
+
+ Does not close any streams except those it opens itself
+ (on a File)."
+ {:added "1.2"}
+ [input output & opts]
+ (do-copy input output (when opts (apply hash-map opts))))
+
+(defn ^String as-relative-path
+ "Take an as-file-able thing and return a string if it is
+ a relative path, else IllegalArgumentException."
+ {:added "1.2"}
+ [x]
+ (let [^File f (as-file x)]
+ (if (.isAbsolute f)
+ (throw (IllegalArgumentException. (str f " is not a relative path")))
+ (.getPath f))))
+
+(defn ^File file
+ "Returns a java.io.File from string or file args."
+ {:added "1.2"}
+ ([arg]
+ (as-file arg))
+ ([parent child]
+ (File. ^File (as-file parent) ^String (as-relative-path child)))
+ ([parent child & more]
+ (reduce file (file parent child) more)))
+
+(defn delete-file
+ "Delete file f. Raise an exception if it fails unless silently is true."
+ {:added "1.2"}
+ [f & [silently]]
+ (or (.delete (file f))
+ silently
+ (throw (java.io.IOException. (str "Couldn't delete " f)))))
+
+(defn delete-file-recursively
+ "Delete file f. If it's a directory, recursively delete all its contents.
+ Raise an exception if any deletion fails unless silently is true."
+ {:added "1.2"}
+ [f & [silently]]
+ (let [f (file f)]
+ (if (.isDirectory f)
+ (doseq [child (.listFiles f)]
+ (delete-file-recursively child silently)))
+ (delete-file f silently)))
+
+(defn make-parents
+ "Create all parent directories of file. Pass extra args
+ as you would to file."
+ {:added "1.2"}
+ [f & more]
+ (.mkdirs (.getParentFile ^File (apply file f more))))
+
+(defn ^URL resource
+ "Returns the URL for a named resource. Use the context class loader
+ if no loader is specified."
+ {:added "1.2"}
+ ([n] (resource n (.getContextClassLoader (Thread/currentThread))))
+ ([n ^ClassLoader loader] (.getResource loader n)))