Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Moved offbytwo.beanhelpers to clojure.java.data

  • Loading branch information...
commit bb4c0a6788bbfcc95b72c0ed1142a38021121cc0 1 parent daef16e
@cosmin cosmin authored
View
1  .gitignore
@@ -0,0 +1 @@
+/target
View
21 README.md
@@ -1,6 +1,25 @@
# java.data
-Future home of Java beans and properties support from contrib.
+Future home of Java beans and properties support from contrib. Currently contains functions for recursively converting Java beans to Clojure and vice versa.
+
+## Example
+
+(use 'clojure.java.data)
+
+(to-java YourJavaClass clojure-property-map)
+(from-java javaValue)
+
+## Extending
+
+;; Representing an instance of YourJavaClass in a Clojure data structure
+(defmethod from-java YourJavaClass [instance]
+ ; your custom logic for turing this instance into a clojure data structure
+)
+
+;; Constructing an instance of YourJavaClass from a Clojure data structure
+(defmethod to-java [YourJavaClass clojure.lang.APersistentMap] [clazz props]
+ ; your custom logic for constructing an instance from a property map
+)
## License
View
28 pom.xml
@@ -0,0 +1,28 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>java.data</artifactId>
+ <version>0.0.1-SNAPSHOT</version>
+ <name>${project.artifactId}</name>
+
+ <url>https://github.com/clojure/java.data/</url>
+ <description>Utilities for working with Java beans from Clojure</description>
+
+ <parent>
+ <groupId>org.clojure</groupId>
+ <artifactId>pom.contrib</artifactId>
+ <version>0.0.21</version>
+ </parent>
+
+ <developers>
+ <developer>
+ <name>Cosmin Stejerean</name>
+ </developer>
+ </developers>
+
+ <scm>
+ <connection>scm:git:git@github.com:clojure/java.data.git</connection>
+ <developerConnection>scm:git:git@github.com:clojure/java.data.git</developerConnection>
+ <url>git@github.com:clojure/java.data.git</url>
+ </scm>
+</project>
View
134 src/main/clojure/clojure/java/data.clj
@@ -0,0 +1,134 @@
+;; Copyright (c) Cosmin Stejerean. All rights reserved. The use and
+;; distribution terms for this software are covered by the Eclipse Public
+;; License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) which can
+;; be found in the file epl-v10.html at the root of this distribution. By
+;; using this software in any fashion, you are agreeing to be bound by the
+;; terms of this license. You must not remove this notice, or any other,
+;; from this software.
+
+(ns
+ ^{:author "Cosmin Stejerean",
+ :doc "A Clojure interface to sql databases via jdbc."}
+ clojure.java.data)
+
+(defmulti to-java (fn [destination-type value] [destination-type (class value)]))
+(defmulti from-java class)
+
+(defn- get-property-descriptors [clazz]
+ (.getPropertyDescriptors (java.beans.Introspector/getBeanInfo clazz)))
+
+;; getters
+
+(defn- is-getter [method]
+ (and method (= 0 (alength (. method (getParameterTypes))))))
+
+(defn- make-getter-fn [method]
+ (fn [instance]
+ (from-java (.invoke method instance nil))))
+
+(defn- add-getter-fn [the-map prop-descriptor]
+ (let [name (.getName prop-descriptor)
+ method (.getReadMethod prop-descriptor)]
+ (if (and (is-getter method) (not (= "class" name)))
+ (assoc the-map (keyword name) (make-getter-fn method))
+ the-map)))
+
+
+;; setters
+
+(defn- is-setter [method]
+ (and method (= 1 (alength (. method (getParameterTypes))))))
+
+(defn- get-setter-type [method]
+ (get (.getParameterTypes method) 0))
+
+(defn- make-setter-fn [method]
+ (fn [instance value]
+ (.invoke method instance (into-array [(to-java (get-setter-type method) value)]))))
+
+(defn- add-setter-fn [the-map prop-descriptor]
+ (let [name (.getName prop-descriptor)
+ method (.getWriteMethod prop-descriptor)]
+ (if (is-setter method)
+ (assoc the-map (keyword name) (make-setter-fn method))
+ the-map)))
+
+
+;; common to-java definitions
+
+(defmethod to-java :default [_ value] value)
+
+(defmethod to-java [Enum String] [enum value]
+ (.invoke (.getDeclaredMethod enum "valueOf" (into-array [String])) nil (into-array [value])))
+
+(defmethod to-java [Object clojure.lang.APersistentMap] [clazz props]
+ "Convert a Clojure map to the specified class using reflection to set the properties"
+ (let [instance (.newInstance clazz)
+ setter-map (reduce add-setter-fn {} (get-property-descriptors clazz))]
+ (doseq [[key value] props]
+ (let [setter (get setter-map (keyword key))]
+ (if (nil? setter)
+ (println "WARNING: Cannot set value for " key " because there is no setter.")
+ (apply setter [instance value]))))
+ instance))
+
+(defmethod to-java [BigInteger Object] [_ value] (biginteger value))
+
+;; common from-java definitions
+
+(defmethod from-java Object [instance]
+ "Convert a Java object to a Clojure map"
+ (try
+ (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)])))
+ (catch Exception e (println "Error trying to convert " instance e))))
+
+(doseq [clazz [String Character Byte Short Integer Long Float Double Boolean BigInteger BigDecimal]]
+ (derive clazz ::do-not-convert))
+
+(defmethod from-java ::do-not-convert [value] value)
+(prefer-method from-java ::do-not-convert Object)
+
+(defmethod from-java Iterable [instance] (for [each (seq instance)] (from-java each)))
+(prefer-method from-java Iterable Object)
+
+(defmethod from-java java.util.Map [instance] (into {} instance))
+(prefer-method from-java java.util.Map Iterable)
+
+(defmethod from-java nil [_] nil)
+(defmethod from-java Enum [enum] (str enum))
+
+;; definitions for interfacting with XMLGregorianCalendar
+
+(defmethod to-java [javax.xml.datatype.XMLGregorianCalendar clojure.lang.APersistentMap] [clazz props]
+ "Create an XMLGregorianCalendar object given the following keys :year :month :day :hour :minute :second :timezone"
+ (let [instance (.newInstance clazz)
+ undefined javax.xml.datatype.DatatypeConstants/FIELD_UNDEFINED
+ getu #(get %1 %2 undefined)]
+ (doto instance
+ (.setYear (getu props :year))
+ (.setMonth (getu props :month))
+ (.setDay (getu props :day))
+ (.setHour (getu props :hour))
+ (.setMinute (getu props :minute))
+ (.setSecond (getu props :second))
+ (.setTimezone (getu props :timezone)))))
+
+(defmethod from-java javax.xml.datatype.XMLGregorianCalendar [obj]
+ "Turn an XMLGregorianCalendar object into a clojure map of year, month, day, hour, minute, second and timezone "
+ (let [date {:year (.getYear obj)
+ :month (.getMonth obj)
+ :day (.getDay obj)}
+ time {:hour (.getHour obj)
+ :minute (.getMinute obj)
+ :second (.getSecond obj)}
+ tz {:timezone (.getTimezone obj)}
+ is-undefined? #(= javax.xml.datatype.DatatypeConstants/FIELD_UNDEFINED %1)]
+ (conj {}
+ (if-not (is-undefined? (:year date))
+ date)
+ (if-not (is-undefined? (:hour time))
+ time)
+ (if-not (is-undefined? (:timezone tz))
+ tz))))
View
0  src/test/clojure/.gitignore
No changes.
View
34 src/test/clojure/clojure/java/test_data.clj
@@ -0,0 +1,34 @@
+;; Copyright (c) Cosmin Stejerean. All rights reserved. The use and
+;; distribution terms for this software are covered by the Eclipse Public
+;; License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) which can
+;; be found in the file epl-v10.html at the root of this distribution. By
+;; using this software in any fashion, you are agreeing to be bound by the
+;; terms of this license. You must not remove this notice, or any other,
+;; from this software.
+
+(ns clojure.java.test-data
+ (:use clojure.java.data)
+ (:use clojure.test)
+ (:import (clojure.java.data.test Person Address State)))
+
+(deftest clojure-to-java
+ (let [person (to-java Person {:name "Bob"
+ :age 30
+ :address {:line1 "123 Main St"
+ :city "Dallas"
+ :state "TX"
+ :zip "75432"}})]
+ (is (= "Bob" (.getName person)))
+ (is (= 30 (.getAge person)))
+ (is (= "123 Main St" (.. person getAddress getLine1)))
+ (is (= "Dallas" (.. person getAddress getCity)))
+ (is (= State/TX (.. person getAddress getState)))
+ (is (= "75432" (.. person getAddress getZip)))))
+
+(deftest java-to-clojure
+ (let [address (new Address "123 Main St" "Dallas" State/TX "75432")
+ person (from-java (Person. "Bob" (biginteger 30) address))]
+ (is (= "Bob" (:name person)))
+ (is (= 30 (:age person)))
+ (is (= "123 Main St" (:line1 (:address person))))
+ (is (= "TX" (:state (:address person))))))
View
0  src/test/java/.gitignore
No changes.
View
50 src/test/java/clojure/java/data/test/Address.java
@@ -0,0 +1,50 @@
+package clojure.java.data.test;
+
+public class Address {
+ private String line1;
+ private String city;
+ private State state;
+ private String zip;
+
+ public Address() {
+ }
+
+ public Address(String line1, String city, State state, String zip) {
+ this.line1 = line1;
+ this.city = city;
+ this.state = state;
+ this.zip = zip;
+ }
+
+ public String getLine1() {
+ return line1;
+ }
+
+ public void setLine1(String line1) {
+ this.line1 = line1;
+ }
+
+ public String getCity() {
+ return city;
+ }
+
+ public void setCity(String city) {
+ this.city = city;
+ }
+
+ public State getState() {
+ return state;
+ }
+
+ public void setState(State state) {
+ this.state = state;
+ }
+
+ public String getZip() {
+ return zip;
+ }
+
+ public void setZip(String zip) {
+ this.zip = zip;
+ }
+}
View
42 src/test/java/clojure/java/data/test/Person.java
@@ -0,0 +1,42 @@
+package clojure.java.data.test;
+
+import java.math.BigInteger;
+
+public class Person {
+ private String name;
+ private BigInteger age;
+ private Address address;
+
+ public Person() {
+ }
+
+ public Person(String name, BigInteger age, Address address) {
+ this.name = name;
+ this.age = age;
+ this.address = address;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public BigInteger getAge() {
+ return age;
+ }
+
+ public void setAge(BigInteger age) {
+ this.age = age;
+ }
+
+ public Address getAddress() {
+ return address;
+ }
+
+ public void setAddress(Address address) {
+ this.address = address;
+ }
+}
View
55 src/test/java/clojure/java/data/test/State.java
@@ -0,0 +1,55 @@
+package clojure.java.data.test;
+
+public enum State {
+ AL,
+ AK,
+ AZ,
+ AR,
+ CA,
+ CO,
+ CT,
+ DE,
+ DC,
+ FL,
+ GA,
+ HI,
+ ID,
+ IL,
+ IN,
+ IA,
+ KS,
+ KY,
+ LA,
+ ME,
+ MD,
+ MA,
+ MI,
+ MN,
+ MS,
+ MO,
+ MT,
+ NE,
+ NV,
+ NH,
+ NJ,
+ NM,
+ NY,
+ NC,
+ ND,
+ OH,
+ OK,
+ OR,
+ PA,
+ RI,
+ SC,
+ SD,
+ TN,
+ TX,
+ UT,
+ VT,
+ VA,
+ WA,
+ WV,
+ WI,
+ WY
+}
Please sign in to comment.
Something went wrong with that request. Please try again.