Hello all,

below is the code for an utility macro. It wraps all public static
functions of a class in closure functions and imports them into the
current namespace. Clojure name will be prefix-'method name'.

Example: (import-static \"foo\" java.lang.Math)

And yes, I saw static import from contrib :) - the difference here is
that my code creates clojure functions that can be passed to other
functions.

This is just an exercise for learning clojure macros, so I would be
very grateful for any comments on the code, suggestions on how to
improve it, etc ...

It is easier to read from bottom and up ...

I have not implemented multimethod support yet, so currently only the
first of overloaded methods will be imported.

Thank you for your help,
Boris

------------------------------------------
;;; some utility funcitons

(defn mk-counter
  "returns a function that for each returns a successive integer
   for each invocation."
  ([] (mk-counter 0))
  ([start]
     (let [n (atom start)]
       (fn [] (- (swap! n inc) 1)))))

(defn seq-to-multimap
  "takes a sequence s of possibly repeating elements
   and converts it to a map, where keys are obtained
   by applying key-fn to elements of s and values are
   sequences of all elements of s with a given key"
  [s key-fn]
  (reduce
   (fn [m el]
     (let [key (key-fn el)]
       (assoc m key
              (conj (m key []) el))))
   {} s))

;;; static import helper functions

(def primitive-type-translation { "bool" "Boolean"
                                  "char" "Character"
                                  "long" "Long"
                                  "int" "Integer"
                                  "short" "Short"
                                  "byte" "Byte"
                                  "float" "Float"
                                  "double" "Double"})

(defn resolve-primitive-type [#^String t] (primitive-type-translation
t t))

(defn type-2-symbol
  "resolves type name into a symbol, converts primitive types
   to boxed types"
  [type]
  (symbol (resolve-primitive-type (.getCanonicalName type))))

(defn get-ps-methods
  "returns all public static methods of a class c"
  [c]
  (let [methods (.getMethods c)]
    (filter #(let [mod (.getModifiers %1)]
               (and (java.lang.reflect.Modifier/isStatic mod)
                    (java.lang.reflect.Modifier/isPublic mod))) methods)))

(defn get-methods-multimap
  "returns a multimap of publi static methods of a class c"
  [c]
  (seq-to-multimap (get-ps-methods c) #(.getName %1)))

(defn make-java-method-symbol
  "returns a symbol that corresponds to a fully qualified
   name of the java method"
  [#^java.lang.reflect.Method method]
  (symbol (str
           (.. method getDeclaringClass getName)
           "/"
           (.getName method))))

(defn get-method-args
  "returns a java Class[] array of method's argument types"
  [method]
  (.getParameterTypes method))

(defn mk-param [param count]
  "returns clojure expression for the parameter declaration with the
type"
  (with-meta (symbol (str "param" count))
             {:tag (type-2-symbol param)}))

(defn mk-params-list [method]
  "returns clojure expression for the parameter declarations of a
funciton"
  (let [c (mk-counter 0)]
    (into [] (map #(mk-param %1 (c)) (get-method-args method)))))

(defn mk-call-exp [method]
  "returns clojure expression for the function call.
   Example: (java.lang.Math/min param0 param1) "
  (let [c (mk-counter 0)]
    (cons (make-java-method-symbol method)
          (map (fn [param] (symbol (str "param" (c))))
               (get-method-args method)))))

(defn mk-func-name [prefix method]
  (symbol (str prefix "-" (.getName method))))

(defn mk-func-def [prefix method]
  `(defn ~(mk-func-name prefix method)
         ~(mk-params-list method)
         ~(mk-call-exp method)))

;;; the macro itself

(defmacro import-static
  "wraps all public static functions of a class in
   closure functions and imports them into the current
   namespace. Clojure name will be prefix-'method name'.

   Example: (import-static \"foo\" java.lang.Math)

   TODO: multimethod support, currently only the first
         of overloaded methods will be imported. "
  [prefix class]
  `(do ~@(map (fn [[name [method & rest]]]
                (mk-func-def prefix method))
              (get-methods-multimap
               (. Class forName (str class))))))

----------------------------------------------------------------
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To post to this group, send email to clojure@googlegroups.com
To unsubscribe from this group, send email to 
clojure+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/clojure?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to