com.samskivert.depot.util.RuntimeUtil Maven / Gradle / Ivy
//
// Depot library - a Java relational persistence library
// https://github.com/threerings/depot/blob/master/LICENSE
package com.samskivert.depot.util;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.sql.Timestamp;
import java.util.Date;
import java.util.List;
import java.util.Map;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.samskivert.depot.PersistentRecord;
import com.samskivert.depot.util.Tuple2;
import static com.google.common.base.Preconditions.checkArgument;
/**
* Creates functions that map persistent records to runtime counterparts and vice versa. A few bits
* of magic are provided to help in mapping from the persistent world to the runtime work. Namely:
*
* {@link Timestamp} and {@link java.sql.Date} are automatically converted to and from {@link
* Date} if necessary.
*
*
You can define custom getter and setter methods in your persistent class like so:
*
* public class FooRecord extends PersistentRecord
* {
* public long monkeyStamp;
*
* public Date getMonkeyStamp ()
* {
* return new Date(monkeyStamp);
* }
*
* public void setMonkeyStamp (Date value)
* {
* monkeyStamp = value.getTime();
* }
* }
*
*
* The conversion methods must be named get
and set
followed by the
* name of your field with the first letter changed to a capital and their type signatures must
* exactly match those of the persistent and runtime types.
*/
public class RuntimeUtil
{
/**
* Creates a function that creates an instance of R and initializes all accessible (ie. public)
* fields of R from fields of P with matching name. If the function is applied to null, null
* will be returned. Fields of P that do not exist in R will be ignored. See the class
* documentation for a list of field conversions that will be made automatically and the
* mechanism for performing other conversions.
*/
public static
Function
makeToRuntime (
Class
pclass, final Class rclass)
{
final Field[] rfields = getRuntimeFields(rclass);
final Getter[] getters = getPersistentGetters(pclass, rfields);
return new Function() {
public R apply (P record) {
if (record == null) {
return null;
}
try {
R object = rclass.newInstance();
for (int ii = 0, ll = rfields.length; ii < ll; ii++) {
rfields[ii].set(object, getters[ii].get(record));
}
return object;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
};
}
/**
* Creates a function that creates an instance of P and initializes all accessible (ie. public)
* fields of P from fields of R with matching name. If the function is applied to null a
* NullPointerException will be thrown. Fields of P that do not exist in R will be left as
* default. Note: the types of the fields must match exactly.
*/
public static Function makeToRecord (
Class rclass, final Class pclass)
{
final Field[] rfields = getRuntimeFields(rclass);
final Setter[] setters = getPersistentSetters(pclass, rfields);
return new Function() {
public P apply (R object) {
if (object == null) {
throw new NullPointerException(
"Cannot convert null runtime record to " + pclass.getSimpleName());
}
try {
P record = pclass.newInstance();
for (int ii = 0, ll = rfields.length; ii < ll; ii++) {
setters[ii].set(record, rfields[ii].get(object));
}
return record;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
};
}
protected static Field[] getRuntimeFields (Class> rclass)
{
List fields = Lists.newArrayList();
for (Field field : rclass.getFields()) {
int mods = field.getModifiers();
if (!Modifier.isStatic(mods) && Modifier.isPublic(mods)) {
fields.add(field);
}
}
return fields.toArray(new Field[fields.size()]);
}
protected static Getter[] getPersistentGetters (Class> pclass, Field[] rfields)
{
Field[] pfields = getPersistentFields(pclass, rfields);
Getter[] getters = new Getter[rfields.length];
for (int ii = 0; ii < rfields.length; ii++) {
getters[ii] = makeGetter(pclass, pfields[ii], rfields[ii]);
}
return getters;
}
protected static Setter[] getPersistentSetters (Class> pclass, Field[] rfields)
{
Field[] pfields = getPersistentFields(pclass, rfields);
Setter[] setters = new Setter[rfields.length];
for (int ii = 0; ii < rfields.length; ii++) {
setters[ii] = makeSetter(pclass, pfields[ii], rfields[ii]);
}
return setters;
}
protected static Getter makeGetter (Class> pclass, final Field pfield, Field rfield)
{
// if there's a custom getter method for the field, use that foremost
String getter = makeMethodName("get", rfield.getName());
try {
final Method method = pclass.getMethod(getter);
if (method.getReturnType().equals(rfield.getType())) {
return new Getter() {
public Object get (Object object) throws Exception {
return method.invoke(object);
}
};
}
} catch (NoSuchMethodException nsme) {
// no problem, keep on truckin'
}
// if we have no persistent field for the runtime field, we're now out of luck
checkArgument(pfield != null,
"Cannot create mapping for %s. Neither %s.%s nor %s %s() exist.",
rfield, pclass.getSimpleName(), rfield.getName(), rfield.getType(), getter);
// if the fields match exactly, return a getter that just gets the field
if (rfield.getType().equals(pfield.getType())) {
return new Getter() {
public Object get (Object object) throws Exception {
return pfield.get(object);
}
};
}
// if we can convert from the persistent type to the runtime type, do that
final Function