package com.googlecode.objectify.impl.translate;
import com.google.common.primitives.Primitives;
import com.googlecode.objectify.impl.Path;
import com.googlecode.objectify.impl.TypeUtils;
/**
* <p>Numbers are funky in the datastore. You can save numbers of any size, but they always retrieve as Long.
* For the hell of it, we also handle String in the datastore by trying to parse it.</p>
*
* <p>Not a ValueTranslatorFactory because Numbers are not assignable to primitives. Java lame.</p>
*
* @author Jeff Schnitzer <jeff@infohazard.org>
*/
public class NumberTranslatorFactory implements TranslatorFactory<Number, Object>
{
@Override
public Translator<Number, Object> create(TypeKey<Number> tk, CreateContext ctx, Path path) {
final Class<?> clazz = Primitives.wrap(tk.getTypeAsClass());
if (!TypeUtils.isAssignableFrom(Number.class, clazz))
return null;
return new ValueTranslator<Number, Object>(Object.class, clazz) {
@Override
protected Number loadValue(Object value, LoadContext ctx, Path path) throws SkipException {
if (value instanceof String) {
try {
return coerceNumber(Long.valueOf((String)value), clazz);
} catch (NumberFormatException ex) {}
try {
return coerceNumber(Double.valueOf((String)value), clazz);
} catch (NumberFormatException ex) {}
}
else if (value instanceof Number) {
return coerceNumber((Number)value, clazz);
}
path.throwIllegalState("Don't know how to translate " + value + " to a number");
return null; // never gets here
}
@Override
protected Object saveValue(Number value, boolean index, SaveContext ctx, Path path) throws SkipException {
return value;
}
};
}
/**
* Coerces the value to be a number of the specified type; needed because
* all numbers come back from the datastore as Long/Double and this screws up
* any type that expects something smaller. We don't need to worry about primitive
* types because we wrapped the class earlier.
*/
private Number coerceNumber(Number value, Class<?> type)
{
if (type == Number.class) return value;
else if (type == Byte.class) return value.byteValue();
else if (type == Short.class) return value.shortValue();
else if (type == Integer.class) return value.intValue();
else if (type == Long.class) return value.longValue();
else if (type == Float.class) return value.floatValue();
else if (type == Double.class) return value.doubleValue();
else throw new IllegalArgumentException(); // should be impossible
}
}