Hi,
Not every bean is immutable, so you cannot cache its values by default. If
reading from the bean is expensive for you, either create a normal hash-map
with into {} and select-keys, or use memoize.
user=> (def b (bean (java.util.Date.)))
#'user/b
user=> (def hm (into {} (select-keys b [:year :month :date])))
#'user/hm
user=> hm
{:date 30, :month 0, :year 114}
user=> (def mb (memoize b))
#'user/mb
user=> (mb :day)
4
JW
On Thu, Jan 30, 2014 at 2:35 PM, pron <[email protected]> wrote:
> 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 [email protected]
> Note that posts from new members are moderated - please be patient with
> your first post.
> To unsubscribe from this group, send email to
> [email protected]
> 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 [email protected].
> For more options, visit https://groups.google.com/groups/opt_out.
>
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to [email protected]
Note that posts from new members are moderated - please be patient with your
first post.
To unsubscribe from this group, send email to
[email protected]
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 [email protected].
For more options, visit https://groups.google.com/groups/opt_out.