The bean function is a very useful Java interop feature that provides a 
read-only view of a Java Bean as a Clojure map.
As it stands, the function performs introspection on the bean's class 
whenever the function is called:

(defn bean
  "Takes a Java object and returns a read-only implementation of the
  map abstraction based upon its JavaBean properties."
  {:added "1.0"}
  [^Object x]
  (let [c (. x (getClass))
        pmap (reduce1 (fn [m ^java.beans.PropertyDescriptor pd]
                         (let [name (. pd (getName))
                               method (. pd (getReadMethod))]
                           (if (and method (zero? (alength (. method 
(getParameterTypes)))))
                             (assoc m (keyword name) (fn [] 
(clojure.lang.Reflector/prepRet (.getPropertyType pd) (. method (invoke x 
nil)))))
                             m)))
                     {}
                     (seq (.. java.beans.Introspector
                              (getBeanInfo c)
                              (getPropertyDescriptors))))
        v (fn [k] ((pmap k)))
        snapshot (fn []
                   (reduce1 (fn [m e]
                             (assoc m (key e) ((val e))))
                           {} (seq pmap)))]
    (proxy [clojure.lang.APersistentMap]
           []
      (containsKey [k] (contains? pmap k))
      (entryAt [k] (when (contains? pmap k) (new clojure.lang.MapEntry k (v 
k))))
      (valAt ([k] (when (contains? pmap k) (v k)))
             ([k default] (if (contains? pmap k) (v k) default)))
      (cons [m] (conj (snapshot) m))
      (count [] (count pmap))
      (assoc [k v] (assoc (snapshot) k v))
      (without [k] (dissoc (snapshot) k))
      (seq [] ((fn thisfn [plseq]
                  (lazy-seq
                   (when-let [pseq (seq plseq)]
                     (cons (new clojure.lang.MapEntry (first pseq) (v (first 
pseq)))
                           (thisfn (rest pseq)))))) (keys pmap))))))




I propose to cache the pmap value for each class using JDK 7's 
ClassValue<http://docs.oracle.com/javase/7/docs/api/>. 
 Here's a proposed implementation:

(def ^:private  ^java.lang.ClassValue bean-class-value
      (proxy [java.lang.ClassValue]
             []
        (computeValue [c]
          (reduce (fn [m ^java.beans.PropertyDescriptor pd]
                     (let [name (. pd (getName))
                           method (. pd (getReadMethod))
                           type (.getPropertyType pd)]
                       (if (and method (zero? (alength (. method 
(getParameterTypes)))))
                         (assoc m (keyword name) (fn [x] 
(clojure.lang.Reflector/prepRet type (. method (invoke x nil)))))
                         m)))
                   {}
                   (seq (.. java.beans.Introspector
                            (getBeanInfo c)
                            (getPropertyDescriptors)))))))
 
(defn bean
  "Takes a Java object and returns a read-only implementation of the
  map abstraction based upon its JavaBean properties."
  {:added "1.0"}
  [^Object x]
  (let [c (. x (getClass))
        pmap (.get  bean-class-value c)
        v (fn [k] ((pmap k) x))
        snapshot (fn []
                   (reduce (fn [m e]
                              (assoc m (key e) ((val e) x)))
                            {} (seq pmap)))]
    (proxy [clojure.lang.APersistentMap]
           []
      (containsKey [k] (contains? pmap k))
      (entryAt [k] (when (contains? pmap k) (new clojure.lang.MapEntry k (v 
k))))
      (valAt ([k] (when (contains? pmap k) (v k)))
        ([k default] (if (contains? pmap k) (v k) default)))
      (cons [m] (conj (snapshot) m))
      (count [] (count pmap))
      (assoc [k v] (assoc (snapshot) k v))
      (without [k] (dissoc (snapshot) k))
      (seq [] ((fn thisfn [plseq]
                 (lazy-seq
                   (when-let [pseq (seq plseq)]
                     (cons (new clojure.lang.MapEntry (first pseq) (v (first 
pseq)))
                           (thisfn (rest pseq)))))) (keys pmap))))))

 

 

-- 
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
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Reply via email to