From 75bf0ce1eb4e33ba26f0a78ca6523fac11ef5e0b Mon Sep 17 00:00:00 2001
From: Jim Blomo <jim.blomo+github@gmail.com>
Date: Sat, 14 Apr 2012 09:18:56 -0700
Subject: [PATCH] adds class signature support for gen-class implement/extends parameterized types (generics) CLJ-970

---
 src/clj/clojure/genclass.clj                    |   15 ++++++++++++++-
 test/clojure/test_clojure/genclass.clj          |   17 ++++++++++++++++-
 test/clojure/test_clojure/genclass/examples.clj |   13 +++++++++++++
 3 files changed, 43 insertions(+), 2 deletions(-)

diff --git a/src/clj/clojure/genclass.clj b/src/clj/clojure/genclass.clj
index 2b1237e..7a8f004 100644
--- a/src/clj/clojure/genclass.clj
+++ b/src/clj/clojure/genclass.clj
@@ -127,6 +127,13 @@
         impl-cname (.. impl-pkg-name (replace "." "/") (replace \- \_))
         ctype (. Type (getObjectType cname))
         iname (fn [^Class c] (.. Type (getType c) (getInternalName)))
+        dname (fn [^Class c] (.. Type (getType c) (getDescriptor)))
+        signature (fn [^Class c params] (if (empty? params)
+                                          (dname c)
+                                          (. (dname c)
+                                             (replace ";" (str \< 
+                                                               (apply str (map (comp dname the-class) params))
+                                                               \> \;)))))
         totype (fn [^Class c] (. Type (getType c)))
         to-types (fn [cs] (if (pos? (count cs))
                             (into-array (map totype cs))
@@ -136,6 +143,12 @@
                             (into-array (replicate n obj-type))
                             (make-array Type 0)))
         super-type ^Type (totype super)
+        ; generate a signature if we're using paramaterized generics
+        class-signature (let [symbols (cons extends implements)]
+                          (when (some #(get (meta %) :parameters) symbols)
+                            (apply str (map #(signature % (get (meta %2) :parameters))
+                                            supers
+                                            symbols))))
         init-name (str init)
         post-init-name (str post-init)
         factory-name (str factory)
@@ -241,7 +254,7 @@
         ]
                                         ;start class definition
     (. cv (visit (. Opcodes V1_5) (+ (. Opcodes ACC_PUBLIC) (. Opcodes ACC_SUPER))
-                 cname nil (iname super)
+                 cname class-signature (iname super)
                  (when-let [ifc (seq interfaces)]
                    (into-array (map iname ifc)))))
 
diff --git a/test/clojure/test_clojure/genclass.clj b/test/clojure/test_clojure/genclass.clj
index 2d95c17..b440030 100644
--- a/test/clojure/test_clojure/genclass.clj
+++ b/test/clojure/test_clojure/genclass.clj
@@ -13,13 +13,17 @@
   (:import [clojure.test_clojure.genclass.examples
             ExampleClass
             ExampleAnnotationClass
+            ExampleGenericSuperClass
+            ExampleGenericInterfaceClass
+            ExampleGenericInterfaceSuperClass
             ArrayDefInterface
             ArrayGenInterface]
 
            [java.lang.annotation ElementType
                                  Retention
                                  RetentionPolicy
-                                 Target]))
+                                 Target]
+           [java.lang.reflect Type]))
 
 (deftest arg-support
   (let [example (ExampleClass.)
@@ -35,6 +39,17 @@
     (is (= #'clojure.test-clojure.genclass.examples/-toString
            (get-field ExampleClass 'toString__var)))))
 
+(deftest parameterized-generics
+  (testing "tracking parameters for generic super classes and interfaces for java reflection"
+           (is (= [String Integer]
+                  (-> ExampleGenericSuperClass .getGenericSuperclass .getActualTypeArguments vec)))
+           (is (= [String Integer]
+                  (-> ExampleGenericInterfaceSuperClass .getGenericSuperclass .getActualTypeArguments vec)))
+           (is (= String
+                  (-> ExampleGenericInterfaceClass .getGenericInterfaces first .getActualTypeArguments first)))
+           (is (= String
+                  (-> ExampleGenericInterfaceSuperClass .getGenericInterfaces first .getActualTypeArguments first)))))
+
 ;todo - fix this, it depends on the order of things out of a hash-map
 #_(deftest test-annotations
   (let [annot-class ExampleAnnotationClass
diff --git a/test/clojure/test_clojure/genclass/examples.clj b/test/clojure/test_clojure/genclass/examples.clj
index 158622f..626dc2b 100644
--- a/test/clojure/test_clojure/genclass/examples.clj
+++ b/test/clojure/test_clojure/genclass/examples.clj
@@ -41,6 +41,19 @@
                                                              java.lang.annotation.ElementType/PARAMETER]}
                            String] void]])
 
+(gen-class :name clojure.test_clojure.genclass.examples.ExampleGenericSuperClass
+  :prefix "super-"
+  :extends ^{:parameters [String Integer]} java.util.HashMap)
+
+(gen-class :name clojure.test_clojure.genclass.examples.ExampleGenericInterfaceClass
+  :prefix "interface-"
+  :implements [^{:parameters [String]} Iterable])
+
+(gen-class :name clojure.test_clojure.genclass.examples.ExampleGenericInterfaceSuperClass
+  :prefix "interface-super-"
+  :extends ^{:parameters [String Integer]} java.util.HashMap
+  :implements [^{:parameters [String]} Iterable])
+
 (definterface ArrayDefInterface
   ; primitive array sugar
   (^void takesByteArray [^bytes a])
-- 
1.7.1

