diff options
author | scgilardi <scgilardi@gmail.com> | 2008-11-13 18:55:39 +0000 |
---|---|---|
committer | scgilardi <scgilardi@gmail.com> | 2008-11-13 18:55:39 +0000 |
commit | 04eb37e3ebb622e2799f1a6be48dd8054495620f (patch) | |
tree | 6dfe1cc2a34b453a0f79b396741da39e2ce72ed5 | |
parent | f0ce2b7230177b8580bbcf0851790582a1d5e91b (diff) |
minimal port to Clojure SVN 1094+ of seq-utils import-static trace enum javalog duck-streams str-utils. (they all load again)
-rw-r--r-- | src/clojure/contrib/duck_streams.clj | 119 | ||||
-rw-r--r-- | src/clojure/contrib/enum.clj | 46 | ||||
-rw-r--r-- | src/clojure/contrib/import_static.clj | 60 | ||||
-rw-r--r-- | src/clojure/contrib/javalog.clj | 97 | ||||
-rw-r--r-- | src/clojure/contrib/seq_utils.clj | 70 | ||||
-rw-r--r-- | src/clojure/contrib/str_utils.clj | 62 | ||||
-rw-r--r-- | src/clojure/contrib/trace.clj | 54 |
7 files changed, 508 insertions, 0 deletions
diff --git a/src/clojure/contrib/duck_streams.clj b/src/clojure/contrib/duck_streams.clj new file mode 100644 index 00000000..a1936676 --- /dev/null +++ b/src/clojure/contrib/duck_streams.clj @@ -0,0 +1,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'). + + +(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)))) + diff --git a/src/clojure/contrib/enum.clj b/src/clojure/contrib/enum.clj new file mode 100644 index 00000000..1986387d --- /dev/null +++ b/src/clojure/contrib/enum.clj @@ -0,0 +1,46 @@ +;;; enum.clj -- Java enum classes in Clojure + +;; by Stuart Sierra, http://www.stuartsierra.com/ +;; May 29, 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 helps define Java Enums, introduced in Java 1.5. Use it +;; when you need to define an enum to pass to a Java method. +;; +;; This file depends on genclass.clj in the Clojure distribution. + + +(ns clojure.contrib.enum) + +(defmacro defenum + "Generates and loads a subclass of java.lang.Enum, then + defs symbols as enumerated instances of that class. + + Example: (defenum my.package.MyEnum FOO BAR) + ;; FOO and BAR are now instances of MyEnum + + Java equivalent: enum MyEnum { FOO, BAR }; + + Caveats: + 1. The generated class has no values() method. + 2. The generated class returns false for Class.isEnum(). + 3. Enum.valueOf(Class, String) will not work. + 4. Redefining an enum is allowed, but enumeration resets + to zero." + [class & symbols] + ;; Can't load a class twice, so check first: + (try (. Class (forName (str class))) + (catch java.lang.ClassNotFoundException e + (gen-and-load-class (str class) :extends java.lang.Enum))) + (cons 'do + (map (fn [sym val] + `(def ~sym (new ~class ~(str sym) ~val))) + symbols (iterate inc 0)))) diff --git a/src/clojure/contrib/import_static.clj b/src/clojure/contrib/import_static.clj new file mode 100644 index 00000000..c8015c8a --- /dev/null +++ b/src/clojure/contrib/import_static.clj @@ -0,0 +1,60 @@ +;;; import_static.clj -- import static Java methods/fields into Clojure + +;; by Stuart Sierra, http://stuartsierra.com/ +;; June 1, 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. + + + +(ns clojure.contrib.import-static + (:use clojure.set)) + +(defmacro import-static + "Imports the named static fields and/or static methods of the class + as (private) symbols in the current namespace. + + Example: + user=> (import-static java.lang.Math PI sqrt) + nil + user=> PI + 3.141592653589793 + user=> (sqrt 16) + 4.0 + + Note: The class name must be fully qualified, even if it has already + been imported. Static methods are defined as MACROS, not + first-class fns." + [class & fields-and-methods] + (let [only (set (map str fields-and-methods)) + the-class (. Class forName (str class)) + static? (fn [x] + (. java.lang.reflect.Modifier + (isStatic (. x (getModifiers))))) + statics (fn [array] + (set (map (memfn getName) + (filter static? array)))) + all-fields (statics (. the-class (getFields))) + all-methods (statics (. the-class (getMethods))) + fields-to-do (intersection all-fields only) + methods-to-do (intersection all-methods only) + make-sym (fn [string] + (with-meta (symbol string) {:private true})) + import-field (fn [name] + (list 'def (make-sym name) + (list '. class (symbol name)))) + import-method (fn [name] + (list 'defmacro (make-sym name) + '[& args] + (list 'list ''. (list 'quote class) + (list 'apply 'list + (list 'quote (symbol name)) + 'args))))] + `(do ~@(map import-field fields-to-do) + ~@(map import-method methods-to-do)))) diff --git a/src/clojure/contrib/javalog.clj b/src/clojure/contrib/javalog.clj new file mode 100644 index 00000000..be662498 --- /dev/null +++ b/src/clojure/contrib/javalog.clj @@ -0,0 +1,97 @@ +;;; javalog.clj -- convenient access to java.util.logging in 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 some convenience functions for using the Java +;; logging framework from Clojure. It is oriented towards simple +;; development and debugging rather than complex production +;; environments. + + + +(ns clojure.contrib.javalog + (:import + (java.util.logging Logger Level ConsoleHandler + FileHandler SimpleFormatter))) + +(def + #^{:tag Logger + :doc "The current java.util.logging.Logger. By default, the + global logger, modified by 'with-logger'."} + *logger* + (. Logger + (getLogger + (. Logger GLOBAL_LOGGER_NAME)))) + +(defmacro log-level + "Translates 'level' (a lower-case keyword) into a static field of + java.util.logging.Level, by name. + + Example: (log-level :severe) => java.util.logging.Level.SEVERE + + If 'level' is not a keyword, it is assumed to be a user-defined + instance of java.util.logging.Level and is returned unchanged." + [level] + (if (keyword? level) + `(. java.util.logging.Level + ~(symbol (. (name level) (toUpperCase)))) + level)) + +(defn root-logger + "Returns the root Logger instance." + ([] (root-logger *logger*)) + ([logger] (let [parent (. logger (getParent))] + (if parent + (recur parent) + logger)))) + +(defn set-console-log-level + "Attempts to set the level of the current logger and the root + ConsoleHandler to 'level' (a java.util.logging.Level). Useful for + debugging at the REPL." + [level] + (let [console-handler + (some (fn [h] (if (instance? ConsoleHandler h) h)) + (. (root-logger) (getHandlers)))] + (if console-handler + (do (. *logger* (setLevel level)) + (. console-handler (setLevel level))) + (throw (new Exception "No ConsoleHandler on root logger."))))) + +(defn add-log-file + "Attaches a log file, using SimpleFormatter, with the given level, + to the named logger. 'level' defaults to ALL. Note: multiple + invocations will create multiple log files, with numbers appended to + the names." + ([logger-name filename] + (add-log-file logger-name filename (. Level ALL))) + ([logger-name filename level] + (let [logger (. Logger (getLogger logger-name)) + handler (new FileHandler filename)] + (. handler (setFormatter (new SimpleFormatter))) + (. handler (setLevel level)) + (. logger (addHandler handler))))) + +(defmacro with-logger + "Executes 'body' with *logger* bound to a logger with the given name + and level. 'level' is expanded with 'log-level'." + [logger-name level & body] + `(binding [*logger* (. Logger (getLogger ~logger-name))] + (. *logger* (setLevel (log-level ~level))) + ~@body)) + +(defmacro log + "Logs a message to *logger*. 'level' is expanded with 'log-level'. + Example: (log :severe \"Bad argument: \" object)" + [level & strings] + `(. *logger* (log (log-level ~level) (str ~@strings)))) diff --git a/src/clojure/contrib/seq_utils.clj b/src/clojure/contrib/seq_utils.clj new file mode 100644 index 00000000..4a8e7c57 --- /dev/null +++ b/src/clojure/contrib/seq_utils.clj @@ -0,0 +1,70 @@ +;;; seq_utils.clj -- Sequence utilities for Clojure + +;; by Stuart Sierra, http://stuartsierra.com/ +;; last updated August 12, 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. + + +(ns clojure.contrib.seq-utils) + + +;; 'flatten' written by Rich Hickey, +;; see http://groups.google.com/group/clojure/msg/385098fabfcaad9b +(defn flatten + "Takes any nested combination of sequential things (lists, vectors, + etc.) and returns their contents as a single, flat sequence." + [x] + (let [s? #(instance? clojure.lang.Sequential %)] + (filter (complement s?) (tree-seq s? seq x)))) + +(defn separate + "Returns a vector: + [ (filter f s), (filter (complement f) s) ]" + [f s] + [(filter f s) (filter (complement f) s)]) + +(defn includes? + "Returns true if s contains something equal (with =) to x." + [x s] + (if (some (fn [y] (= y x)) s) + true false)) + +(defn indexed + "Returns a lazy sequence of [index, item] pairs, where items come + from 's' and indexes count up from zero. + + (indexed '(a b c d)) => ([0 a] [1 b] [2 c] [3 d])" + [s] + (map vector (iterate inc 0) s)) + +;; group-by written by Rich Hickey; +;; see http://paste.lisp.org/display/64190 +(defn group-by [f coll] + "Returns a sorted map of the elements of coll keyed by the result of + f on each element. The value at each key will be a vector of the + corresponding elements, in the order they appeared in coll." + (reduce + (fn [ret x] + (let [k (f x)] + (assoc ret k (conj (get ret k []) x)))) + (sorted-map) coll)) + +;; partition-by written by Rich Hickey; +;; see http://paste.lisp.org/display/64190 +(defn partition-by [f coll] + "Applies f to each value in coll, splitting it each time f returns + a new value. Returns a lazy seq of lazy seqs." + (when-let [s (seq coll)] + (let [fv (f (first s)) + ends (drop-while #(= fv (f %)) (rest s)) + tw (fn this [s] + (when-not (identical? s ends) + (lazy-cons (first s) (this (rest s)))))] + (lazy-cons (tw s) (partition-by f ends))))) diff --git a/src/clojure/contrib/str_utils.clj b/src/clojure/contrib/str_utils.clj new file mode 100644 index 00000000..fb5404e7 --- /dev/null +++ b/src/clojure/contrib/str_utils.clj @@ -0,0 +1,62 @@ +;;; str_utils.clj -- string utilities 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. + + +(ns clojure.contrib.str-utils + (:import (java.util.regex Pattern))) + +(defn re-split + "Splits the string on instances of 'pattern'. Returns a sequence of + strings. Optional 'limit' argument is the maximum number of + splits. Like Perl's 'split'." + ([#^Pattern pattern string] (seq (. pattern (split string)))) + ([#^Pattern pattern string limit] (seq (. pattern (split string limit))))) + +(defn re-partition + "Splits the string into a lazy sequence of substrings, alternating + between substrings that match the patthern and the substrings + between the matches. The sequence always starts with the substring + before the first match, or an empty string if the beginning of the + string matches. + + For example: (re-partition #\"[a-z]+\" \"abc123def\") + + Returns: (\"\" \"abc\" \"123\" \"def\")" + [#^Pattern re string] + (let [m (re-matcher re string)] + ((fn step [prevend] + (if (.find m) + (lazy-cons (.subSequence string prevend (.start m)) + (lazy-cons (re-groups m) + (step (+ (.start m) (count (.group m)))))) + (when (< prevend (.length string)) + (list (.subSequence string prevend (.length string)))))) + 0))) + +(defn re-gsub + "Replaces all instances of 'pattern' in 'string' with + 'replacement'. Like Ruby's 'String#gsub'." + [#^Pattern regex replacement #^String string] + (.. regex (matcher string) (replaceAll replacement))) + +(defn re-sub + "Replaces the first instance of 'pattern' in 'string' with + 'replacement'. Like Ruby's 'String#sub'." + [#^Pattern regex replacement #^String string] + (.. regex (matcher string) (replaceFirst replacement))) + +(defn str-join + "Returns a string of all elements in 'sequence', separated by + 'separator'. Like Perl's 'join'." + [separator sequence] + (apply str (interpose separator sequence))) diff --git a/src/clojure/contrib/trace.clj b/src/clojure/contrib/trace.clj new file mode 100644 index 00000000..ee7ddd59 --- /dev/null +++ b/src/clojure/contrib/trace.clj @@ -0,0 +1,54 @@ +;;; trace.clj -- simple call-tracing macros for Clojure + +;; by Stuart Sierra, http://stuartsierra.com/ +;; June 9, 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 simple "tracing" macros to help you see what your +;; code is doing. + + +(ns clojure.contrib.trace) + +(def + #^{:doc "PrintStream for trace output. Defaults to System.err."} + *trace-out* (. System err)) + +(defmacro trace + "Prints value of expr to standard error and returns it. Can be + inserted anywhere without affecting surrounding code. Optional + 'name' argument can be used to identify what is being traced." + ([expr] + `(let [value# ~expr] + (. *trace-out* (println + (str "TRACE: " (pr-str value#)))) + value#)) + ([name expr] + `(let [value# ~expr] + (. *trace-out* (println + (str "TRACE " ~name ": " (pr-str value#)))) + value#))) + +(defmacro deftrace + "Use in place of defn; traces each call/return of this fn, including + arguments." + [name & definition] + `(let [f# (fn ~@definition)] + (defn ~name [& args#] + (let [id# (gensym "t")] ; identifier for this invocation + (. *trace-out* + (println (str "TRACE " id# ": " ~(str name) + " called with " (pr-str args#)))) + (let [value# (apply f# args#)] ; call original fn + (. *trace-out* + (println (str "TRACE " id# ": " ~(str name) + " returned " (pr-str value#)))) + value#))))) |