Hi, I've been working on some Java libraries wrappers lately and came
up with patterns on how to clojurize them. I'm wondering if you guys
got other such patterns?

Here's what I came up with:

(import 'java.lang.reflect.Modifier)

(defn clojurize-name [name]
  (apply str
    (interpose "-"
      (map #(.toLowerCase %)
        (re-seq #"\w[a-z]+" name)))))

(defn clojurize-constant-name [name]
  (.replace
   (.toLowerCase (str name)) "_" "-"))

(defn enum->map [names constants]
  (into {} (map #(vector (keyword (clojurize-constant-name %1)) %2)
                names
                constants)))

(defn get-static-fields [klass]
  (filter #(Modifier/isStatic (.getModifiers %))
          (.getFields klass)))

(defn wrap-enum [klass]
  (if (isa? klass Enum)
    (let [cs (.getEnumConstants klass)]
      (enum->map (map str cs) cs))
    (let [cs (get-static-fields klass)]
      (enum->map (map #(.getName %) cs)
                 (map #(.get % nil) cs)))))

(defn call-method [name obj & args]
  (apply list (symbol (str "." name)) obj args))

(defmacro wrap-methods-into [obj klass & [flter rename]]
  (let [methods (filter (eval flter)
                        (.getDeclaredMethods (resolve klass)))]
    (reduce #(into %1 (let [name (.getName %2)]
                        {((eval rename) name)
                         (call-method name obj)}))
            {}
            methods)))

(defn getter? [method]
  (and (= 0 (count (.getParameterTypes method)))
       (re-seq #"get[A-Z$]" (.getName method))))

(defmacro wrap-getters-with [obj klass]
  `(wrap-methods-into ~obj ~klass
                      getter?
                      (comp keyword
                            (fn [s#] (.replace s# "get-" ""))
                            clojurize-name)))

I use this code mainly to write a Java Sound API wrapper, here's some
examples:

(def float-controls (wrap-enum javax.sound.sampled.FloatControl$Type))

user> (pprint float-controls)
{:balance #<Type Balance>,
 :reverb-return #<Type Reverb Return>,
 :master-gain #<Type Master Gain>,
 :aux-return #<Type AUX Return>,
 :pan #<Type Pan>,
 :reverb-send #<Type Reverb Send>,
 :sample-rate #<Type Sample Rate>,
 :aux-send #<Type AUX Send>,
 :volume #<Type Volume>}

and...

(defn format-info [audio-format]
  (into (wrap-getters-with audio-format
javax.sound.sampled.AudioFormat)
        {:endianness (if (.isBigEndian audio-format)
                       :big-endian
                       :little-endian)}))

user> (pprint (-> "g:/test.mp3" ->stream ->format format-info))
{:channels 2,
 :frame-rate 38.28125,
 :frame-size -1,
 :sample-rate 44100.0,
 :sample-size-in-bits -1,
 :encoding #<MpegEncoding MPEG1L3>,
 :endianness :big-endian}

I'm still not sure about how to properly name the macros, if anybody
got a better idea, I sure want to ear it.

-- 
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
Note that posts from new members are moderated - please be patient with your 
first post.
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