On Wed, Nov 16, 2011 at 17:28, Ben Mabey <b...@benmabey.com> wrote:
> Hi,
> I would like to be able to add metadata to arbitrary java objects that have
> already been instantiated.  I know that you can use proxy to add metadata to
> objects that you create but in my case the object already exists (i.e. it is
> returned from another method call outside of my control).
>
> It seems like the best solution would be to create a delegate class/object
> that wraps the original one.  Being able to write something like this would
> be ideal:
>
> (defn wrap-with-meta
>  ([obj]
>     (wrap-with-meta obj nil))
>  ([obj meta]
>   (delegate obj
>             clojure.lang.IObj
>             (withMeta [this new-meta] (wrap-with-meta obj
>
>
>  new-meta))
>             (meta [this] meta))))
>
> The delegate function would operate very similar to proxy, but instead of
> taking a class it takes an object.  A class would be created that extends
> the object's class, just like a class is generated for proxy. However,
> instead of stubs being generated that call super the stubs would delegate to
> the given object.  (Also note, I am also using reify-like syntax since I
> prefer that to the syntax in proxy.)
>
> I'm curious to hear people's thoughts on this approach.  I'd also be really
> interested to see how other people have addressed this in the past (I doubt
> I'm the first one to run into this).
>
> I know that clojure mentality is to avoid wrappers, but I don't see this as
> being much different than what proxy already does.  Of course, I may be
> missing something... if so, please enlighten me. :)
>
> Thanks,
> Ben

Here's an approach that may be of use: don't store the metadata in a
Map instead of decorating the Object with it. This map should use
object identity, not equality and should hold its keys weakly so that
it prevent collection of  objects that otherwise would be garbage.
Safe to use concurrently would also be a plus. Conveniently, Google's
Guava library provides such a thing. Here's a sketch:

== project.clj ========================================

(defproject pojometa "0.0.1-SNAPSHOT"
  :dependencies [[org.clojure/clojure "1.3.0"]
                 [com.google.guava/guava "10.0.1"]])

== src/pojometa/core.clj ==============================

(ns pojometa.core
    (:import [com.google.common.collect MapMaker]))

(def meta-map
    (-> (MapMaker.) .weakKeys .makeMap))

(defn meta* [o]
    (if (instance? clojure.lang.IMeta o)
        (clojure.core/meta o)
        (.get meta-map o)))

(defn with-meta* [o m]
    (if (instance? clojure.lang.IMeta o)
        (clojure.core/with-meta o m)
        (do (.put meta-map o m)
            o)))

== usage ==============================================

pojometa.core=> (def o (Object.)) ;; arbitrary java object
pojometa.core=> (meta* (with-meta* o {:foo true}))
{:foo true}

;; also "does the right thing" for Clojure types that
;; already know how to have metadata.

pojometa.core=> (meta* (with-meta* {} {:bar 1}))
{:bar 1}

== END

Hope that helps,
Ben

-- 
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