package org.sql2o.converters;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.joda.time.LocalTime;
import org.sql2o.converters.joda.DateTimeConverter;
import org.sql2o.converters.joda.LocalDateConverter;
import org.sql2o.converters.joda.LocalTimeConverter;
import org.sql2o.tools.FeatureDetector;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.UUID;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* Static class used to register new converters.
* Also used internally by sql2o to lookup a converter.
*/
@SuppressWarnings("unchecked")
public class Convert {
private static final ReentrantReadWriteLock rrwl = new ReentrantReadWriteLock();
private static final ReentrantReadWriteLock.ReadLock rl = rrwl.readLock();
private static final ReentrantReadWriteLock.WriteLock wl = rrwl.writeLock();
private static volatile EnumConverterFactory registeredEnumConverterFactory = new DefaultEnumConverterFactory();
private static Map<Class<?>, Converter<?>> registeredConverters = new HashMap<Class<?>, Converter<?>>();
private static void processProvider(ConvertersProvider convertersProvider) {
convertersProvider.fill(registeredConverters);
}
private static void fillDefaults(Map<Class<?>, Converter<?>> mapToFill) {
mapToFill.put(Integer.class, new IntegerConverter(false));
mapToFill.put(int.class, new IntegerConverter(true));
mapToFill.put(Double.class, new DoubleConverter(false));
mapToFill.put(double.class, new DoubleConverter(true));
mapToFill.put(Float.class, new FloatConverter(false));
mapToFill.put(float.class, new FloatConverter(true));
mapToFill.put(Long.class, new LongConverter(false));
mapToFill.put(long.class, new LongConverter(true));
mapToFill.put(Short.class, new ShortConverter(false));
mapToFill.put(short.class, new ShortConverter(true));
mapToFill.put(Byte.class, new ByteConverter(false));
mapToFill.put(byte.class, new ByteConverter(true));
mapToFill.put(BigDecimal.class, new BigDecimalConverter());
mapToFill.put(String.class, new StringConverter());
mapToFill.put(java.util.Date.class,DateConverter.instance);
mapToFill.put(java.sql.Date.class,
new AbstractDateConverter<java.sql.Date>(java.sql.Date.class) {
@Override
protected java.sql.Date fromMilliseconds(long millisecond) {
return new java.sql.Date(millisecond);
}
});
mapToFill.put(java.sql.Time.class,
new AbstractDateConverter<java.sql.Time>(java.sql.Time.class) {
@Override
protected java.sql.Time fromMilliseconds(long millisecond) {
return new java.sql.Time(millisecond);
}
});
mapToFill.put(java.sql.Timestamp.class,
new AbstractDateConverter<java.sql.Timestamp>(java.sql.Timestamp.class) {
@Override
protected java.sql.Timestamp fromMilliseconds(long millisecond) {
return new java.sql.Timestamp(millisecond);
}
});
BooleanConverter booleanConverter = new BooleanConverter();
mapToFill.put(Boolean.class, booleanConverter);
mapToFill.put(boolean.class, booleanConverter);
ByteArrayConverter byteArrayConverter = new ByteArrayConverter();
//it's impossible to cast Byte[].class <-> byte[].class
// and I'm too lazy to implement converter for Byte[].class
// since it's really doesn't wide-used
// otherwise someone already detect this error
//mapToFill.put(Byte[].class, byteArrayConverter);
mapToFill.put(byte[].class, byteArrayConverter);
InputStreamConverter inputStreamConverter = new InputStreamConverter();
mapToFill.put(InputStream.class, inputStreamConverter);
mapToFill.put(ByteArrayInputStream.class, inputStreamConverter);
mapToFill.put(UUID.class, new UUIDConverter());
if (FeatureDetector.isJodaTimeAvailable()) {
mapToFill.put(DateTime.class, new DateTimeConverter());
mapToFill.put(LocalTime.class, new LocalTimeConverter());
mapToFill.put(LocalDate.class, new LocalDateConverter());
}
}
static {
fillDefaults(registeredConverters);
ServiceLoader<ConvertersProvider> loader = ServiceLoader.load(ConvertersProvider.class);
for (ConvertersProvider provider : loader) {
processProvider(provider);
}
}
@SuppressWarnings("UnusedDeclaration")
@Deprecated
public static Converter getConverter(Class clazz) throws ConverterException {
return throwIfNull(clazz, getConverterIfExists(clazz));
}
public static <E> Converter<E> throwIfNull(Class<E> clazz, Converter<E> converter) throws ConverterException {
if (converter == null) {
throw new ConverterException("No converter registered for class: " + clazz.getName());
}
return converter;
}
public static <E> Converter<E> getConverterIfExists(Class<E> clazz) {
Converter c;
rl.lock();
try {
c = registeredConverters.get(clazz);
} finally {
rl.unlock();
}
if (c != null) return c;
if (clazz.isEnum()) {
return registeredEnumConverterFactory.newConverter((Class) clazz);
}
return null;
}
@SuppressWarnings("UnusedDeclaration")
@Deprecated
public static void registerConverter(Class clazz, Converter converter) {
wl.lock();
try {
registerConverter0(clazz, converter);
} finally {
wl.unlock();
}
}
private static void registerConverter0(Class clazz, Converter converter) {
registeredConverters.put(clazz, converter);
}
@SuppressWarnings("UnusedDeclaration")
public static void registerEnumConverter(EnumConverterFactory enumConverterFactory) {
if (enumConverterFactory == null) throw new IllegalArgumentException();
registeredEnumConverterFactory = enumConverterFactory;
}
}