package org.molgenis.data;
import org.apache.commons.lang3.StringUtils;
import org.molgenis.data.convert.DateToStringConverter;
import org.molgenis.data.convert.StringToDateConverter;
import org.molgenis.data.meta.model.Attribute;
import org.molgenis.util.ApplicationContextProvider;
import org.molgenis.util.ListEscapeUtils;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.ConverterNotFoundException;
import org.springframework.core.convert.support.DefaultConversionService;
import java.sql.Date;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import static java.util.stream.StreamSupport.stream;
@edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "NP_BOOLEAN_RETURN_NULL", justification = "We want to return Boolean.TRUE, Boolean.FALSE or null")
public class DataConverter
{
private static ConversionService conversionService;
public static boolean canConvert(Object source, Class<?> targetType)
{
try
{
convert(source, targetType);
return true;
}
catch (Exception e)
{
return false;
}
}
@SuppressWarnings("unchecked")
public static <T> T convert(Object source, Class<T> targetType)
{
if (source == null)
{
return null;
}
if (targetType.isAssignableFrom(source.getClass()))
{
return (T) source;
}
return getConversionService().convert(source, targetType);
}
/**
* Convert value to the type based on the given attribute.
*
* @param source value to convert
* @param attr attribute that defines the type of the converted value
* @return converted value or the input value if the attribute type is a reference type
*/
public static Object convert(Object source, Attribute attr)
{
switch (attr.getDataType())
{
case BOOL:
return toBoolean(source);
case XREF:
case CATEGORICAL:
case CATEGORICAL_MREF:
case MREF:
case FILE:
case ONE_TO_MANY:
return source;
case COMPOUND:
throw new UnsupportedOperationException();
case DATE:
return toDate(source);
case DATE_TIME:
return toUtilDate(source);
case DECIMAL:
return toDouble(source);
case INT:
return toInt(source);
case LONG:
return toLong(source);
default:
return toString(source);
}
}
public static String toString(Object source)
{
if (source == null) return null;
if (source instanceof String) return (String) source;
if (source instanceof Entity)
{
Object labelValue = ((Entity) source).getLabelValue();
return labelValue != null ? labelValue.toString() : null;
}
if (source instanceof List)
{
StringBuilder sb = new StringBuilder();
for (Object obj : (List<?>) source)
{
if (sb.length() > 0) sb.append(",");
sb.append(toString(obj));
}
return sb.toString();
}
if (getConversionService() == null) return source.toString();
try
{
return convert(source, String.class);
}
catch (ConverterNotFoundException e)
{
return source.toString();
}
}
public static Integer toInt(Object source)
{
if (source == null) return null;
if (source instanceof Integer) return (Integer) source;
return convert(source, Integer.class);
}
public static Long toLong(Object source)
{
if (source == null) return null;
if (source instanceof Long) return (Long) source;
return convert(source, Long.class);
}
public static Boolean toBoolean(Object source)
{
if (source == null) return null;
if (source instanceof Boolean) return (Boolean) source;
return convert(source, Boolean.class);
}
public static Double toDouble(Object source)
{
if (source == null) return null;
if (source instanceof Double) return (Double) source;
return convert(source, Double.class);
}
public static java.sql.Date toDate(Object source)
{
if (source == null) return null;
if (source instanceof java.sql.Date) return (java.sql.Date) source;
if (source instanceof java.util.Date) return new java.sql.Date(((java.util.Date) source).getTime());
return new java.sql.Date(convert(source, java.util.Date.class).getTime());
}
public static java.util.Date toUtilDate(Object source)
{
if (source == null) return null;
if (source instanceof java.util.Date) return (java.util.Date) source;
return convert(source, java.util.Date.class);
}
public static Timestamp toTimestamp(Object source)
{
if (source == null) return null;
else if (source instanceof Timestamp) return (Timestamp) source;
else if (source instanceof Date) return new Timestamp(((Date) source).getTime());
return new Timestamp(convert(source, java.util.Date.class).getTime());
}
public static Entity toEntity(Object source)
{
if (source == null) return null;
if (source instanceof Entity) return (Entity) source;
return convert(source, Entity.class);
}
@SuppressWarnings("unchecked")
public static Iterable<Entity> toEntities(Object source)
{
if (source == null) return null;
if (source instanceof Iterable) return (Iterable<Entity>) source;
return null;
}
@SuppressWarnings("unchecked")
public static List<String> toList(Object source)
{
if (source == null) return null;
else if (source instanceof Iterable<?>)
{
return stream(((Iterable<?>) source).spliterator(), false).map(obj ->
{
Object objValue;
if (obj instanceof Entity)
{
objValue = ((Entity) obj).getIdValue();
}
else
{
objValue = obj;
}
return DataConverter.toString(objValue);
}).collect(Collectors.toList());
}
else if (source instanceof String) return ListEscapeUtils.toList((String) source);
else return ListEscapeUtils.toList(source.toString());
}
@SuppressWarnings("unchecked")
public static List<Object> toObjectList(Object source)
{
if (source == null) return null;
else if (source instanceof List) return (List<Object>) source;
else if (source instanceof String)
{
List<Object> result = new ArrayList<Object>();
for (String str : ((String) source).split(","))
result.add(str);
return result;
}
else return Arrays.asList(new Object[] { source });
}
public static List<Integer> toIntList(Object source)
{
if (source == null) return null;
else if (source instanceof String)
{
List<String> stringList = ListEscapeUtils.toList((String) source);
List<Integer> intList = new ArrayList<Integer>();
for (String s : stringList)
{
if (!StringUtils.isNumeric(s))
{
throw new IllegalArgumentException(s + " is not an integer");
}
intList.add(Integer.parseInt(s));
}
return intList;
}
else if (source instanceof Iterable<?>)
{
ArrayList<Integer> intList = new ArrayList<Integer>();
for (Object o : (Iterable<?>) source)
{
if (o instanceof Entity)
{
o = ((Entity) o).getIdValue();
}
intList.add(toInt(o));
}
return intList;
}
else if (source instanceof Integer) return new ArrayList<Integer>(Arrays.asList((Integer) source));
else return null;
}
private static ConversionService getConversionService()
{
if (conversionService == null)
{
if (ApplicationContextProvider.getApplicationContext() == null)
{
// We are not in a Spring managed environment
conversionService = new DefaultConversionService();
((DefaultConversionService) conversionService).addConverter(new DateToStringConverter());
((DefaultConversionService) conversionService).addConverter(new StringToDateConverter());
}
else
{
conversionService = ApplicationContextProvider.getApplicationContext().getBean(ConversionService.class);
}
}
return conversionService;
}
}