aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/clojure/contrib/duck_streams/duck_streams.clj120
-rw-r--r--src/clojure/contrib/enum/enum.clj47
-rw-r--r--src/clojure/contrib/except/except.clj44
-rw-r--r--src/clojure/contrib/fcase/fcase.clj93
-rw-r--r--src/clojure/contrib/gen_interface/gen_interface.clj179
-rw-r--r--src/clojure/contrib/javalog/javalog.clj98
-rw-r--r--src/clojure/contrib/lazy_seqs/lazy_seqs.clj90
-rw-r--r--src/clojure/contrib/lib/lib.clj483
-rw-r--r--src/clojure/contrib/memoize/memoize.clj32
-rw-r--r--src/clojure/contrib/ns_utils/ns_utils.clj88
-rw-r--r--src/clojure/contrib/pred/pred.clj108
-rw-r--r--src/clojure/contrib/seq_utils/seq_utils.clj79
-rw-r--r--src/clojure/contrib/sql/sql.clj126
-rw-r--r--src/clojure/contrib/str_utils/str_utils.clj44
-rw-r--r--src/clojure/contrib/string/string.clj29
-rw-r--r--src/clojure/contrib/test_is/test_is.clj211
-rw-r--r--src/clojure/contrib/trace/trace.clj55
-rw-r--r--src/clojure/contrib/zip_filter/xml/xml.clj177
-rw-r--r--src/clojure/contrib/zip_filter/zip_filter.clj91
19 files changed, 2194 insertions, 0 deletions
diff --git a/src/clojure/contrib/duck_streams/duck_streams.clj b/src/clojure/contrib/duck_streams/duck_streams.clj
new file mode 100644
index 00000000..855e8db5
--- /dev/null
+++ b/src/clojure/contrib/duck_streams/duck_streams.clj
@@ -0,0 +1,120 @@
+;;; 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/in-ns 'clojure.contrib.duck-streams)
+(clojure/refer 'clojure)
+
+(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/enum.clj b/src/clojure/contrib/enum/enum.clj
new file mode 100644
index 00000000..b417028d
--- /dev/null
+++ b/src/clojure/contrib/enum/enum.clj
@@ -0,0 +1,47 @@
+;;; 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.
+
+
+(clojure/in-ns 'clojure.contrib.enum)
+(clojure/refer 'clojure)
+
+(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/except/except.clj b/src/clojure/contrib/except/except.clj
new file mode 100644
index 00000000..a2fc8521
--- /dev/null
+++ b/src/clojure/contrib/except/except.clj
@@ -0,0 +1,44 @@
+;; Copyright (c) Stephen C. Gilardi. All rights reserved. The use and
+;; distribution terms for this software are covered by the Common Public
+;; License 1.0 (http://opensource.org/licenses/cpl.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.
+;;
+;; except.clj
+;;
+;; scgilardi (gmail)
+;; Created 07 July 2008
+
+(clojure/in-ns 'clojure.contrib.except)
+(clojure/refer 'clojure)
+
+(clojure.contrib.lib/use '(clojure.contrib string))
+
+(defn throw-if
+ "Throws an exception with a message if pred is true. Arguments are:
+
+ pred class? format format-args*
+
+ class is optional and defaults to Exception. If present, it must be a
+ Class in the tree under Throwable with a constructor that takes a single
+ String.
+
+ format is a string as documented for java.util.Formatter.
+
+ format-args are zero or more objects that correspond to the format
+ specifiers in format."
+ [pred & args]
+ (if pred
+ (let [class-present (instance? Class (first args))
+ args (if class-present args (cons Exception args))
+ [class fmt & fmt-args] args
+ ctor (.getConstructor (identity class) (into-array [String]))
+ message (apply format fmt fmt-args)
+ exception (.newInstance ctor (into-array [message]))
+ raw-trace (.getStackTrace exception)
+ boring? #(not= (.getMethodName %) "doInvoke")
+ trace (into-array (drop 2 (drop-while boring? raw-trace)))]
+ (.setStackTrace exception trace)
+ (throw exception))))
diff --git a/src/clojure/contrib/fcase/fcase.clj b/src/clojure/contrib/fcase/fcase.clj
new file mode 100644
index 00000000..8e316f56
--- /dev/null
+++ b/src/clojure/contrib/fcase/fcase.clj
@@ -0,0 +1,93 @@
+;;; fcase.clj -- simple variants of "case" for Clojure
+
+;; by Stuart Sierra <mail@stuartsierra.com>
+;; April 7, 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 a generic "case" macro called "fcase" which takes
+;; the equality-testing function as an argument. It also defines a
+;; traditional "case" macro that tests using "=" and variants that
+;; test for regular expressions and class membership.
+
+
+(clojure/in-ns 'clojure.contrib.fcase)
+(clojure/refer 'clojure)
+
+
+(defmacro fcase
+ "Generic switch/case macro. 'fcase' is short for 'function case'.
+
+ The 'compare-fn' is a fn of two arguments.
+
+ The 'test-expr-clauses' are value-expression pairs without
+ surrounding parentheses, like in Clojure's 'cond'.
+
+ The 'case-value' is evaluated once and cached. Then, 'compare-fn'
+ is called once for each clause, with the clause's test value as its
+ first argument and 'case-value' as its second argument. If
+ 'compare-fn' returns logical true, the clause's expression is
+ evaluated and returned. If 'compare-fn' returns false/nil, we go to
+ the next test value.
+
+ If 'test-expr-clauses' contains an odd number of items, the last
+ item is the default expression evaluated if no case-value matches.
+ If there is no default expression and no case-value matches, fcase
+ returns nil.
+
+ See specific forms of this macro in 'case' and 're-case'.
+
+ The test expressions in 'fcase' are always evaluated linearly, in
+ order. For a large number of case expressions it may be more
+ efficient to use a hash lookup."
+ [compare-fn case-value &
+ test-expr-clauses]
+ (let [test-val-sym (gensym "test_val")
+ test-fn-sym (gensym "test_fn")
+ cond-loop (fn this [clauses]
+ (cond
+ (>= (count clauses) 2)
+ (list 'if (list test-fn-sym (first clauses) test-val-sym)
+ (second clauses)
+ (this (rest (rest clauses))))
+ (= (count clauses) 1) (first clauses)))]
+ (list 'let [test-val-sym case-value, test-fn-sym compare-fn]
+ (cond-loop test-expr-clauses))))
+
+(defmacro case
+ "Like cond, but test-value is compared against the value of each
+ test expression with =. If they are equal, executes the \"body\"
+ expression. Optional last expression is executed if none of the
+ test expressions match."
+ [test-value & clauses]
+ `(fcase = ~test-value ~@clauses))
+
+(defmacro re-case
+ "Like case, but the test expressions are regular expressions, tested
+ with re-find."
+ [test-value & clauses]
+ `(fcase re-find ~test-value ~@clauses))
+
+(defmacro instance-case
+ "Like case, but the test expressions are Java class names, tested with
+ 'instance?'."
+ [test-value & clauses]
+ `(fcase instance? ~test-value ~@clauses))
+
+(defn- in-case-test [test-seq case-value]
+ (some (fn [item] (= item case-value))
+ test-seq))
+
+(defmacro in-case
+ "Like case, but test expressions are sequences. The test expression
+ is true if any item in the sequence is equal (tested with '=') to
+ the test value."
+ [test-value & clauses]
+ `(fcase in-case-test ~test-value ~@clauses))
diff --git a/src/clojure/contrib/gen_interface/gen_interface.clj b/src/clojure/contrib/gen_interface/gen_interface.clj
new file mode 100644
index 00000000..2ab9a601
--- /dev/null
+++ b/src/clojure/contrib/gen_interface/gen_interface.clj
@@ -0,0 +1,179 @@
+; Copyright (c) Chris Houser, July 2008. All rights reserved.
+; The use and distribution terms for this software are covered by the
+; Common Public License 1.0 (http://opensource.org/licenses/cpl.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.
+
+; Functions for generating interface classes, which can then be loaded
+; or saved to a .class file.
+
+(clojure/in-ns 'clojure.contrib.gen-interface)
+(clojure/refer 'clojure)
+
+(import '(clojure.asm ClassWriter Opcodes Type)
+ '(java.io File FileOutputStream IOException))
+
+(defn- asm-type
+ "Returns an asm Type object for c, which may be a primitive class
+ (such as Integer/TYPE), any other class (such as Double), or a
+ fully-qualified class name given as a string or symbol
+ (such as 'java.lang.String)"
+ [c]
+ (if (instance? Class c)
+ (Type/getType c)
+ (Type/getObjectType (.replace (str c) "." "/"))))
+
+(defn- iname
+ "Returns the internal name of given class or class name. Cannot be
+ used for primitive types."
+ [c] (.getInternalName (asm-type c)))
+
+(defstruct #^{:private true} spec-map :cname :iname :extends :methods)
+
+(defn- make-spec
+ "Returns an interface spec object based on the given description.
+ cname is the fully-qualified classname (string or symbol) of the
+ interface to be created.
+ extends is a collection of classes this interface will extend (each
+ may be a string, symbol, or a class). These are followed by the
+ method descriptions, each of which is a vector: [methodName,
+ arg types, return type]"
+ [cname extends & methods]
+ (struct spec-map
+ (str cname)
+ (iname cname)
+ (set (map iname extends))
+ (set (map (fn [[mname pclasses rclass]]
+ [(str mname)
+ (map asm-type pclasses)
+ (asm-type rclass)])
+ methods))))
+
+(defn- spec-from-class
+ "Returns an interface spec object based on the given class."
+ [c]
+ (struct spec-map
+ (.getName c)
+ (iname c)
+ (set (map iname (.getInterfaces c)))
+ (set (map (fn [m]
+ [(.getName m)
+ (map asm-type (.getParameterTypes m))
+ (asm-type (.getReturnType m))])
+ (.getDeclaredMethods c)))))
+
+(def #^{:private true} object-iname (iname Object))
+
+(defn- spec-bytecode
+ "Uses the given interface spec object (such as created by make-spec)
+ to generate a Java interface. Returns a byte array containing the
+ Java bytecode for the interface. You'll almost always want to use
+ gen-interface instead."
+ [{:keys [iname extends methods]}]
+ (let [cv (ClassWriter. ClassWriter/COMPUTE_MAXS)]
+ (. cv visit Opcodes/V1_5 (+ Opcodes/ACC_PUBLIC Opcodes/ACC_ABSTRACT
+ Opcodes/ACC_INTERFACE)
+ iname nil object-iname
+ (when (seq extends)
+ (into-array extends)))
+ (doseq [mname pclasses rclass] methods
+ (. cv visitMethod (+ Opcodes/ACC_PUBLIC Opcodes/ACC_ABSTRACT)
+ mname
+ (Type/getMethodDescriptor rclass (if pclasses
+ (into-array pclasses)
+ (make-array Type 0)))
+ nil nil))
+ (. cv visitEnd)
+ (. cv toByteArray)))
+
+
+(defn- load-interface-bytecode
+ [{:keys [cname] :as spec} bytecode]
+ (let [old-class (try (Class/forName cname) (catch Throwable t nil))]
+ (if old-class
+ (when-not (= spec (spec-from-class old-class))
+ (throw (Exception. (str "A different class named "
+ cname " already loaded"))))
+ (.. clojure.lang.RT
+ ROOT_CLASSLOADER (defineClass cname bytecode)))))
+
+(defn- save-interface-bytecode
+ [path {:keys [cname]} bytecode]
+ (let [file (File. path (str (.replace cname \. File/separatorChar)
+ ".class"))]
+ (try
+ (.createNewFile file)
+ (catch IOException e
+ (throw (Exception. (str "Failed to create " file) e))))
+ (with-open f (FileOutputStream. file)
+ (.write f bytecode))))
+
+(defn gen-and-load-interface
+ "Uses the given interface description to generate a Java interface
+ and immediately load it. make-spec-args is the interface
+ description as documented in make-spec. This function is not
+ generally useful since you'll usually want a .class file in order
+ to write Java code that uses the generated interface -- see
+ gen-interface instead."
+ [& make-spec-args]
+ (let [spec (apply make-spec make-spec-args)]
+ (load-interface-bytecode spec (spec-bytecode spec))))
+
+(defn gen-and-save-interface
+ "Uses the given interface description to generate a Java interface
+ and save it to a .class file. make-spec-args is the interface
+ description as documented in make-spec. The .class file will be
+ written into a sub-directory of the given base path (note that the
+ appropriate sub-directories under path must already exist or this
+ will throw an exception). If you intend to use this interface
+ immediately (for example to refer to it in a later gen-interface or
+ gen-class call), you'll want to use gen-interface instead."
+ [path & make-spec-args]
+ (let [spec (apply make-spec make-spec-args)]
+ (save-interface-bytecode path spec (spec-bytecode spec))))
+
+(defn gen-interface
+ "Uses the given interface description to generate a Java interface,
+ save it to a .class file, and immediately load it so it's ready
+ for use by subsequent gen-interface or gen-class calls. The .class
+ file will be written into a sub-directory of the given base path.
+ make-spec-args is the interface description as documented in
+ make-spec."
+ [path & make-spec-args]
+ (let [spec (apply make-spec make-spec-args)
+ bytecode (spec-bytecode spec)]
+ (load-interface-bytecode spec bytecode)
+ (save-interface-bytecode path spec bytecode)))
+
+(comment
+
+(gen-interface "/tmp" 'net.n01se.Foo [Appendable]
+ ['foo [] Integer]
+ ['bar [Integer/TYPE String] Double])
+
+; re-genning an identical interface doesn't try to load anything
+(gen-interface "/tmp" 'net.n01se.Foo [Appendable]
+ ['foo [] Integer]
+ ['bar [Integer/TYPE String] Double])
+
+; re-genning a different interface throws an exception
+;(gen-interface "/tmp" 'net.n01se.Foo [Appendable]
+; ['foo [] Integer])
+
+; gen-and-save-interface is used directly in this example because I
+; want to refer to a class that's not yet defined in this runtime
+; (Other). This is possible because I specify the class as a quoted
+; symbol, and then don't load it -- but this isn't really recommended.
+; Instead, why not make sure Other is defined -- then you can use
+; gen-interface.
+(gen-and-save-interface "/tmp" 'net.n01se.Bar ['net.n01se.Other Iterable]
+ ['baz [] net.n01se.Foo])
+
+(prn :isInterface (.isInterface (identity net.n01se.Foo)))
+(prn :interfaces (seq (.getGenericInterfaces (identity net.n01se.Foo))))
+(doseq m (seq (.getMethods (identity net.n01se.Foo)))
+ (prn m))
+
+)
diff --git a/src/clojure/contrib/javalog/javalog.clj b/src/clojure/contrib/javalog/javalog.clj
new file mode 100644
index 00000000..2cb12c48
--- /dev/null
+++ b/src/clojure/contrib/javalog/javalog.clj
@@ -0,0 +1,98 @@
+;;; 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.
+
+
+
+(clojure/in-ns 'clojure.contrib.javalog)
+(clojure/refer 'clojure)
+
+(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/lazy_seqs/lazy_seqs.clj b/src/clojure/contrib/lazy_seqs/lazy_seqs.clj
new file mode 100644
index 00000000..9ffd72f4
--- /dev/null
+++ b/src/clojure/contrib/lazy_seqs/lazy_seqs.clj
@@ -0,0 +1,90 @@
+;; Copyright (c) Stephen C. Gilardi. All rights reserved. The use and
+;; distribution terms for this software are covered by the Common Public
+;; License 1.0 (http://opensource.org/licenses/cpl.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.
+;;
+;; lazy-seqs
+;;
+;; == Lazy sequences ==
+;;
+;; primes - based on the "naive" implemention described in [1] plus a
+;; small "wheel" which eliminates multiples of 2, 3, 5, and
+;; 7 from consideration by incrementing past them. Also inspired
+;; by code from Christophe Grand in [2].
+;;
+;; fibs - based on code from Rich Hickey at the Clojure wiki [3]
+;;
+;; powers-of-2 - all the powers of 2
+;;
+;; == Lazy sequence functions ==
+;;
+;; rotations - returns a lazy seq of all the rotations of a seq
+;;
+;; permutations - returns a lazy seq of all the permutations of a seq
+;;
+;; [1] http://www.cs.hmc.edu/~oneill/papers/Sieve-JFP.pdf
+;; [2] http://clj-me.blogspot.com/2008/06/primes.html
+;; [3] http://en.wikibooks.org/wiki/Clojure_Programming#Examples
+;;
+;; scgilardi (gmail)
+;; Created 07 June 2008
+
+(clojure/in-ns 'clojure.contrib.lazy-seqs)
+(clojure/refer 'clojure)
+
+(clojure.contrib.lib/use '(clojure.contrib def))
+
+(defvar primes
+ (lazy-cat [2 3 5 7]
+ (let [primes-from
+ (fn primes-from [n [f & r]]
+ (if (some #(zero? (rem n %))
+ (take-while #(<= (* % %) n) primes))
+ (recur (+ n f) r)
+ (lazy-cons n (primes-from (+ n f) r))))
+ wheel (cycle [2 4 2 4 6 2 6 4 2 4 6 6 2 6 4 2
+ 6 4 6 8 4 2 4 2 4 8 6 4 6 2 4 6
+ 2 6 6 4 2 4 6 2 6 4 2 4 2 10 2 10])]
+ (primes-from 11 wheel)))
+ "A lazy sequence of all the prime numbers.")
+
+(defvar fibs
+ (lazy-cat [0 1]
+ (let [rest-fn
+ (fn rest-fn [a b]
+ (let [next (+ a b)]
+ (lazy-cons next (rest-fn b next))))]
+ (rest-fn 0 1)))
+ "A lazy sequence of all the fibonacci numbers.")
+
+(defvar powers-of-2
+ (lazy-cons 1
+ (let [rest-fn
+ (fn rest-fn [n]
+ (let [next (bit-shift-left n 1)]
+ (lazy-cons next (rest-fn next))))]
+ (rest-fn 1)))
+ "A lazy sequence of all the powers of 2")
+
+(defn rotations
+ "Returns a lazy seq of all rotations of a seq"
+ [x]
+ (if (seq x)
+ (map
+ (fn [n _]
+ (lazy-cat (drop n x) (take n x)))
+ (iterate inc 0) x)
+ (list nil)))
+
+(defn permutations
+ "Returns a lazy seq of all permutations of a seq"
+ [x]
+ (if (seq x)
+ (mapcat
+ (fn [[f & r]]
+ (map #(cons f %) (permutations r)))
+ (rotations x))
+ (list nil)))
diff --git a/src/clojure/contrib/lib/lib.clj b/src/clojure/contrib/lib/lib.clj
new file mode 100644
index 00000000..c5324b07
--- /dev/null
+++ b/src/clojure/contrib/lib/lib.clj
@@ -0,0 +1,483 @@
+;; Copyright (c) Stephen C. Gilardi. All rights reserved. The use and
+;; distribution terms for this software are covered by the Common Public
+;; License 1.0 (http://opensource.org/licenses/cpl.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.
+;;
+;; clojure.contrib.lib (Lib)
+;;
+;; Lib provides functions for loading and managing Clojure source code
+;; contained in Java resources.
+;;
+;; A 'lib' is a unit of Clojure source code contained in a Java resource
+;; and named by a symbol. The lib's name is used to identify the lib and
+;; to locate the resource that contains it.
+;;
+;; Lib provides functions to:
+;;
+;; - find a resource given a classpath-relative path
+;; - load code from a resource given an absolute path
+;; - load libs from resources given lib names
+;; - load namespace definitions given namespace specs
+;; - ensure namespace definitions have been loaded while avoiding
+;; duplicate loads, and
+;; - create a namespace and refer 'clojure into it succinctly
+;;
+;; Symbols, Namespaces, Packages
+;;
+;; Symbols in Clojure are two-part identifiers - with an optional
+;; namespace and a name, both strings. Namespaces are used to distinguish
+;; two symbols that have the same name. Vars are named by symbols that
+;; must have a namespace part, and thus can be considered to be in
+;; namespaces. [From Clojure documentation]
+;;
+;; Packages in Java play a role similar to Clojure namespaces - they
+;; partition the global namespace to allow large programs to avoid name
+;; conflicts. Java defines a mapping from package names to directories
+;; within classpath: components of a package name separated by periods
+;; correspond to components of a classpath-relative path separated by
+;; slashes. Lib uses this same mapping to locate libs and namespaces in
+;; classpath.
+;;
+;; Loading Libs
+;;
+;; A lib's name provides the location of the resource that contains it in
+;; classpath. The resource name is the last component of the lib's path
+;; followed by ".clj". For example, a lib named 'a.b.c is contained in the
+;; resource "<classpath>/a/b/c.clj".
+;;
+;; Loading Namespaces
+;;
+;; To load a namespace definition, Lib loads the 'root lib' for the
+;; namespace. The root lib's name is derived from the namespace name by
+;; repeating its last component. For example, the root lib for the
+;; namespace 'x.y.z is the lib 'x.y.z.z contained in the resource
+;; "<classpath>/x/y/z/z.clj". The namespace definition may be entirely
+;; within the root lib or it may span multiple libs in the hierarchy of
+;; directories at and under the namespace's directory. In the latter case
+;; the root lib must include commands to (directly or indirectly) load the
+;; remaining libs.
+;;
+;; Nsspecs
+;;
+;; An nsspec specifies a namespace to load. It is either a namespace name
+;; or a list containing the namespace name and zero or more options
+;; expressed as squential keywords and values. Nsspec examples:
+;; - 'clojure.contrib.sql
+;; - '(clojure.contrib.sql)
+;; - '(clojure.contrib.sql :exclude (get-connection)).
+;; - '(clojure.contrib.sql :as sql)
+;;
+;; Prefix Lists
+;;
+;; It is common for Clojure code to depend on several libs or namespaces
+;; whose names have one or more initial components in common. When
+;; specifying lib or namespace names for Lib to use, prefix lists can be
+;; used to reduce repetition in the call. A prefix list is a list
+;; containing the shared prefix followed by lib or namespace names and/or
+;; prefix lists with the shared prefix elided. For example, the following
+;; are all equivalent:
+;; (load-libs 'a.b.c 'a.b.d 'a.c.e 'a.c.f) ; all names fully qualified
+;; (load-libs '(a.b c d) '(a.c e f)) ; 'a.b and 'a.c extracted
+;; (load-libs '(a (b c d) (c e f))) ; all common prefixes extracted
+;; Symbols containing explicit periods and the equivalent prefix lists may
+;; be mixed freely.
+;;
+;; Lib Functions
+;;
+;; Resources
+;;
+;; Function: find-resource
+;; Searches available class loaders for a resource, returns URL or nil
+;;
+;; Function: load-resource
+;; Loads Clojure source from an absolute path: URI, URL or String
+;;
+;; Libs
+;;
+;; Function: load-libs
+;; Loads libs by name from arbitrary locations within classpath
+;;
+;; Namespaces
+;;
+;; Function: load-namespaces
+;; Loads namespace definitions by loading namespace root libs
+;;
+;; Function: namespaces
+;; Returns a sorted set symbols naming namespaces that have been loaded
+;; with the :require option - used to track and avoid duplicate loads.
+;;
+;; Function: require
+;; Loads namespace definitions that have not yet been loaded
+;;
+;; Function: use
+;; Requires namespaces and refers to them using clojure/refer
+;;
+;; Function: init-ns
+;; Creates and enters a namespace and refers to 'clojure and