aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorscgilardi <scgilardi@gmail.com>2008-11-13 18:55:39 +0000
committerscgilardi <scgilardi@gmail.com>2008-11-13 18:55:39 +0000
commit04eb37e3ebb622e2799f1a6be48dd8054495620f (patch)
tree6dfe1cc2a34b453a0f79b396741da39e2ce72ed5
parentf0ce2b7230177b8580bbcf0851790582a1d5e91b (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.clj119
-rw-r--r--src/clojure/contrib/enum.clj46
-rw-r--r--src/clojure/contrib/import_static.clj60
-rw-r--r--src/clojure/contrib/javalog.clj97
-rw-r--r--src/clojure/contrib/seq_utils.clj70
-rw-r--r--src/clojure/contrib/str_utils.clj62
-rw-r--r--src/clojure/contrib/trace.clj54
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#)))))