rlm@10: ; Copyright (c) Stuart Halloway & Contributors, April 2009. All rights reserved. rlm@10: ; The use and distribution terms for this software are covered by the rlm@10: ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) rlm@10: ; which can be found in the file epl-v10.html at the root of this distribution. rlm@10: ; By using this software in any fashion, you are agreeing to be bound by rlm@10: ; the terms of this license. rlm@10: ; You must not remove this notice, or any other, from this software. rlm@10: rlm@10: ;; rlm@10: ;; CHANGELOG rlm@10: ;; rlm@10: ;; Most functions deprecated in 1.2. Some already exist in c.c.io, and rlm@10: ;; some replaced by c.c.reflections rlm@10: rlm@10: (ns rlm@10: ^{:author "Stuart Halloway, Stephen C. Gilardi, Shawn Hoover, Perry Trolard, Stuart Sierra", rlm@10: :doc "A set of utilties for dealing with Java stuff like files and properties. rlm@10: rlm@10: Design goals: rlm@10: rlm@10: (1) Ease-of-use. These APIs should be convenient. Performance is secondary. rlm@10: rlm@10: (2) Duck typing. I hate having to think about the difference between rlm@10: a string that names a file, and a File. Ditto for a ton of other rlm@10: wrapper classes in the Java world (URL, InternetAddress). With these rlm@10: APIs you should be able to think about domain equivalence, not type rlm@10: equivalence. rlm@10: rlm@10: (3) No bossiness. I am not marking any of these functions as private rlm@10: the docstrings will tell you the intended usage but do what works for you. rlm@10: rlm@10: Feedback welcome! rlm@10: rlm@10: If something in this module violates the principle of least surprise, please rlm@10: let me (Stu) and the Clojure community know via the mailing list. rlm@10: Contributors: rlm@10: rlm@10: Stuart Halloway rlm@10: Stephen C. Gilardi rlm@10: Shawn Hoover rlm@10: Perry Trolard rlm@10: Stuart Sierra rlm@10: "} rlm@10: clojure.contrib.java-utils rlm@10: (:import [java.io File FileOutputStream] rlm@10: [java.util Properties] rlm@10: [java.net URI URL])) rlm@10: rlm@10: (defmulti relative-path-string rlm@10: "Interpret a String or java.io.File as a relative path string. rlm@10: Building block for clojure.contrib.java-utils/file." rlm@10: {:deprecated "1.2"} rlm@10: class) rlm@10: rlm@10: (defmethod relative-path-string String [^String s] rlm@10: (relative-path-string (File. s))) rlm@10: rlm@10: (defmethod relative-path-string File [^File f] rlm@10: (if (.isAbsolute f) rlm@10: (throw (IllegalArgumentException. (str f " is not a relative path"))) rlm@10: (.getPath f))) rlm@10: rlm@10: (defmulti ^File as-file rlm@10: "Interpret a String or a java.io.File as a File. Building block rlm@10: for clojure.contrib.java-utils/file, which you should prefer rlm@10: in most cases." rlm@10: {:deprecated "1.2"} rlm@10: class) rlm@10: (defmethod as-file String [^String s] (File. s)) rlm@10: (defmethod as-file File [f] f) rlm@10: rlm@10: (defn ^File file rlm@10: "Returns a java.io.File from string or file args." rlm@10: {:deprecated "1.2"} rlm@10: ([arg] rlm@10: (as-file arg)) rlm@10: ([parent child] rlm@10: (File. ^File (as-file parent) ^String (relative-path-string child))) rlm@10: ([parent child & more] rlm@10: (reduce file (file parent child) more))) rlm@10: rlm@10: (defn as-str rlm@10: "Like clojure.core/str, but if an argument is a keyword or symbol, rlm@10: its name will be used instead of its literal representation. rlm@10: rlm@10: Example: rlm@10: (str :foo :bar) ;;=> \":foo:bar\" rlm@10: (as-str :foo :bar) ;;=> \"foobar\" rlm@10: rlm@10: Note that this does not apply to keywords or symbols nested within rlm@10: data structures; they will be rendered as with str. rlm@10: rlm@10: Example: rlm@10: (str {:foo :bar}) ;;=> \"{:foo :bar}\" rlm@10: (as-str {:foo :bar}) ;;=> \"{:foo :bar}\" " rlm@10: {:deprecated "1.2"} rlm@10: ([] "") rlm@10: ([x] (if (instance? clojure.lang.Named x) rlm@10: (name x) rlm@10: (str x))) rlm@10: ([x & ys] rlm@10: ((fn [^StringBuilder sb more] rlm@10: (if more rlm@10: (recur (. sb (append (as-str (first more)))) (next more)) rlm@10: (str sb))) rlm@10: (new StringBuilder ^String (as-str x)) ys))) rlm@10: rlm@10: (defn get-system-property rlm@10: "Get a system property." rlm@10: ([stringable] rlm@10: (System/getProperty (as-str stringable))) rlm@10: ([stringable default] rlm@10: (System/getProperty (as-str stringable) default))) rlm@10: rlm@10: (defn set-system-properties rlm@10: "Set some system properties. Nil clears a property." rlm@10: [settings] rlm@10: (doseq [[name val] settings] rlm@10: (if val rlm@10: (System/setProperty (as-str name) (as-str val)) rlm@10: (System/clearProperty (as-str name))))) rlm@10: rlm@10: (defmacro with-system-properties rlm@10: "setting => property-name value rlm@10: rlm@10: Sets the system properties to the supplied values, executes the body, and rlm@10: sets the properties back to their original values. Values of nil are rlm@10: translated to a clearing of the property." rlm@10: [settings & body] rlm@10: `(let [settings# ~settings rlm@10: current# (reduce (fn [coll# k#] rlm@10: (assoc coll# k# (get-system-property k#))) rlm@10: {} rlm@10: (keys settings#))] rlm@10: (set-system-properties settings#) rlm@10: (try rlm@10: ~@body rlm@10: (finally rlm@10: (set-system-properties current#))))) rlm@10: rlm@10: rlm@10: ; Not there is no corresponding props->map. Just destructure! rlm@10: (defn ^Properties as-properties rlm@10: "Convert any seq of pairs to a java.utils.Properties instance. rlm@10: Uses as-str to convert both keys and values into strings." rlm@10: {:tag Properties} rlm@10: [m] rlm@10: (let [p (Properties.)] rlm@10: (doseq [[k v] m] rlm@10: (.setProperty p (as-str k) (as-str v))) rlm@10: p)) rlm@10: rlm@10: (defn read-properties rlm@10: "Read properties from file-able." rlm@10: [file-able] rlm@10: (with-open [f (java.io.FileInputStream. (file file-able))] rlm@10: (doto (Properties.) rlm@10: (.load f)))) rlm@10: rlm@10: (defn write-properties rlm@10: "Write properties to file-able." rlm@10: {:tag Properties} rlm@10: ([m file-able] (write-properties m file-able nil)) rlm@10: ([m file-able comments] rlm@10: (with-open [^FileOutputStream f (FileOutputStream. (file file-able))] rlm@10: (doto (as-properties m) rlm@10: (.store f ^String comments))))) rlm@10: rlm@10: (defn delete-file rlm@10: "Delete file f. Raise an exception if it fails unless silently is true." rlm@10: {:deprecated "1.2"} rlm@10: [f & [silently]] rlm@10: (or (.delete (file f)) rlm@10: silently rlm@10: (throw (java.io.IOException. (str "Couldn't delete " f))))) rlm@10: rlm@10: (defn delete-file-recursively rlm@10: "Delete file f. If it's a directory, recursively delete all its contents. rlm@10: Raise an exception if any deletion fails unless silently is true." rlm@10: {:deprecated "1.2"} rlm@10: [f & [silently]] rlm@10: (let [f (file f)] rlm@10: (if (.isDirectory f) rlm@10: (doseq [child (.listFiles f)] rlm@10: (delete-file-recursively child silently))) rlm@10: (delete-file f silently))) rlm@10: rlm@10: (defmulti rlm@10: ^{:deprecated "1.2" rlm@10: :doc "Coerces argument (URL, URI, or String) to a java.net.URL." rlm@10: :arglists '([arg])} rlm@10: as-url type) rlm@10: rlm@10: (defmethod as-url URL [x] x) rlm@10: rlm@10: (defmethod as-url URI [^URI x] (.toURL x)) rlm@10: rlm@10: (defmethod as-url String [^String x] (URL. x)) rlm@10: rlm@10: (defmethod as-url File [^File x] (.toURL x)) rlm@10: rlm@10: (defn wall-hack-method rlm@10: "Calls a private or protected method. rlm@10: params is a vector of class which correspond to the arguments to the method rlm@10: obj is nil for static methods, the instance object otherwise rlm@10: the method name is given as a symbol or a keyword (something Named)" rlm@10: {:deprecated "1.2"} rlm@10: [class-name method-name params obj & args] rlm@10: (-> class-name (.getDeclaredMethod (name method-name) (into-array Class params)) rlm@10: (doto (.setAccessible true)) rlm@10: (.invoke obj (into-array Object args)))) rlm@10: rlm@10: (defn wall-hack-field rlm@10: "Access to private or protected field." rlm@10: {:deprecated "1.2"} rlm@10: [class-name field-name obj] rlm@10: (-> class-name (.getDeclaredField (name field-name)) rlm@10: (doto (.setAccessible true)) rlm@10: (.get obj)))