diff --git a/pom.xml b/pom.xml index 6e2f557..f9c2b9c 100644 --- a/pom.xml +++ b/pom.xml @@ -40,7 +40,7 @@ org.clojure tools.logging - 0.1.2 + 0.2.3 diff --git a/src/main/clojure/clojure/java/data.clj b/src/main/clojure/clojure/java/data.clj index 8416bac..5afc725 100644 --- a/src/main/clojure/clojure/java/data.clj +++ b/src/main/clojure/clojure/java/data.clj @@ -61,10 +61,23 @@ (assoc the-map (keyword name) (make-setter-fn method)) the-map))) +(defn- add-array-methods [acls] + (let [cls (.getComponentType acls) + to (fn [_ sequence] (into-array cls (map (partial to-java cls) + sequence))) + from (fn [obj] (map from-java obj))] + (.addMethod to-java [acls Iterable] to) + (.addMethod from-java acls from) + {:to to :from from})) ;; common to-java definitions -(defmethod to-java :default [_ value] value) +(defmethod to-java :default [^Class cls value] + (if (.isArray cls) + ; no method for this array type yet + ((:to (add-array-methods cls)) + cls value) + value)) (defmethod to-java [Enum String] [enum value] (.invoke (.getDeclaredMethod enum "valueOf" (into-array [String])) nil (into-array [value]))) @@ -92,16 +105,39 @@ ;; common from-java definitions -(defmethod from-java Object [instance] +(defmethod from-java :default [^Object instance] "Convert a Java object to a Clojure map" - (let [clazz (.getClass instance) - getter-map (reduce add-getter-fn {} (get-property-descriptors clazz))] - (into {} (for [[key getter-fn] (seq getter-map)] [key (getter-fn instance)])))) + (let [clazz (.getClass instance)] + (if (.isArray clazz) + ((:from (add-array-methods clazz)) + instance) + (let [getter-map (reduce add-getter-fn {} (get-property-descriptors clazz))] + (into {} (for [[key getter-fn] (seq getter-map)] [key (getter-fn instance)])))))) (doseq [clazz [String Character Byte Short Integer Long Float Double Boolean BigInteger BigDecimal]] (derive clazz ::do-not-convert)) +(defmacro ^{:private true} defnumber [box prim prim-getter] + `(let [conv# (fn [_# number#] + (~(symbol (str box) "valueOf") + (. number# ~prim-getter)))] + (.addMethod to-java [~prim Number] conv#) + (.addMethod to-java [~box Number] conv#))) + +(defmacro ^{:private true} defnumbers [& boxes] + (cons `do + (for [box boxes + :let [box-cls (resolve box) + prim-cls (.get (.getField box-cls "TYPE") + box-cls) + _ (assert (class? box-cls) (str box ": no class found")) + _ (assert (class? prim-cls) (str box " has no TYPE field")) + prim-getter (symbol (str (.getName prim-cls) "Value"))]] + `(defnumber ~box ~(symbol (str box) "TYPE") ~prim-getter)))) + +(defnumbers Byte Short Integer Long Float Double) + (defmethod from-java ::do-not-convert [value] value) (prefer-method from-java ::do-not-convert Object) diff --git a/src/test/clojure/clojure/java/test_data.clj b/src/test/clojure/clojure/java/test_data.clj index 068dd11..3b175d2 100644 --- a/src/test/clojure/clojure/java/test_data.clj +++ b/src/test/clojure/clojure/java/test_data.clj @@ -10,7 +10,7 @@ (:use clojure.java.data) (:use [clojure.tools.logging :only (log* info)]) (:use clojure.test) - (:import (clojure.java.data.test Person Address State))) + (:import (clojure.java.data.test Person Address State Primitive))) (deftest clojure-to-java (let [person (to-java Person {:name "Bob" @@ -58,3 +58,25 @@ (is (= 30 (:age person))) (is (= "123 Main St" (:line1 (:address person)))) (is (= "TX" (:state (:address person)))))) + +(deftest primitives + (let [datum {:boolMember true + :boolArray [true false] + :charMember \H + :charArray (map identity "Hello World") + :byteMember 127 + :byteArray [1 2 3] + :shortMember 15000 + :shortArray [13000 14000 15000] + :intMember 18000 + :intArray [1 2 3] + :longMember 60000000 + :longArray [1 2 3] + :floatMember 1.5 + :floatArray [1.5 2.5 3.5] + :doubleMember 1.5 + :doubleArray [1.5 2.0 2.5] + :nestedIntArray [[1 2] [3] [4 5 6] []] + :stringArray ["Argument" "Vector"]}] + (is (= datum + (from-java (to-java Primitive datum)))))) diff --git a/src/test/java/clojure/java/data/test/Primitive.java b/src/test/java/clojure/java/data/test/Primitive.java new file mode 100644 index 0000000..c1d2abf --- /dev/null +++ b/src/test/java/clojure/java/data/test/Primitive.java @@ -0,0 +1,133 @@ +package clojure.java.data.test; + +public class Primitive { + private boolean boolMember; + private boolean[] boolArray; + private char charMember; + private char[] charArray; + + private byte byteMember; + private byte[] byteArray; + private short shortMember; + private short[] shortArray; + private int intMember; + private int[] intArray; + private long longMember; + private long[] longArray; + private float floatMember; + private float[] floatArray; + private double doubleMember; + private double[] doubleArray; + + private int[][] nestedIntArray; + private String[] stringArray; + + public boolean isBoolMember() { + return boolMember; + } + public void setBoolMember(boolean boolMember) { + this.boolMember = boolMember; + } + public boolean[] getBoolArray() { + return boolArray; + } + public void setBoolArray(boolean[] boolArray) { + this.boolArray = boolArray; + } + public char getCharMember() { + return charMember; + } + public void setCharMember(char charMember) { + this.charMember = charMember; + } + public char[] getCharArray() { + return charArray; + } + public void setCharArray(char[] charArray) { + this.charArray = charArray; + } + public byte getByteMember() { + return byteMember; + } + public void setByteMember(byte byteMember) { + this.byteMember = byteMember; + } + public byte[] getByteArray() { + return byteArray; + } + public void setByteArray(byte[] byteArray) { + this.byteArray = byteArray; + } + public short getShortMember() { + return shortMember; + } + public void setShortMember(short shortMember) { + this.shortMember = shortMember; + } + public short[] getShortArray() { + return shortArray; + } + public void setShortArray(short[] shortArray) { + this.shortArray = shortArray; + } + public int getIntMember() { + return intMember; + } + public void setIntMember(int intMember) { + this.intMember = intMember; + } + public int[] getIntArray() { + return intArray; + } + public void setIntArray(int[] intArray) { + this.intArray = intArray; + } + public long getLongMember() { + return longMember; + } + public void setLongMember(long longMember) { + this.longMember = longMember; + } + public long[] getLongArray() { + return longArray; + } + public void setLongArray(long[] longArray) { + this.longArray = longArray; + } + public float getFloatMember() { + return floatMember; + } + public void setFloatMember(float floatMember) { + this.floatMember = floatMember; + } + public float[] getFloatArray() { + return floatArray; + } + public void setFloatArray(float[] floatArray) { + this.floatArray = floatArray; + } + public double getDoubleMember() { + return doubleMember; + } + public void setDoubleMember(double doubleMember) { + this.doubleMember = doubleMember; + } + public double[] getDoubleArray() { + return doubleArray; + } + public void setDoubleArray(double[] doubleArray) { + this.doubleArray = doubleArray; + } + public int[][] getNestedIntArray() { + return nestedIntArray; + } + public void setNestedIntArray(int[][] nestedIntArray) { + this.nestedIntArray = nestedIntArray; + } + public String[] getStringArray() { + return stringArray; + } + public void setStringArray(String[] stringArray) { + this.stringArray = stringArray; + } +}